Compare commits

...

60 Commits

Author SHA1 Message Date
LoveSy
5cbaf2ae11 Use super let to simplify code 2025-08-22 12:05:44 -07:00
topjohnwu
8ebc6207b4 Merge headers 2025-08-22 12:03:47 -07:00
topjohnwu
7848ee616b Cleanup magiskboot main function 2025-08-22 12:03:47 -07:00
topjohnwu
fd193c3cae Simplify ResultExt implementation
Also introduce OptionExt
2025-08-22 12:03:47 -07:00
topjohnwu
36d33c7a85 Make log_err directly return LoggedResult 2025-08-22 12:03:47 -07:00
topjohnwu
5caf28d27c Hide harmless error reporting 2025-08-22 12:03:47 -07:00
topjohnwu
2c39d0234d Fix compression format detection 2025-08-21 12:21:22 -07:00
topjohnwu
c313812129 Simplify magiskboot FFI 2025-08-21 12:21:22 -07:00
topjohnwu
af51880a81 Introduce CmdArgs for argument parsing in Rust 2025-08-21 12:21:22 -07:00
LoveSy
db8d832707 Move magiskboot cli to argh 2025-08-20 21:40:34 -07:00
Wang Han
8dc23d0ead Avoid triggering magisk --zygote-restart twice
We have already used on restart keyword to inject zygote restart, so
triggering it here on prop is not needed.
2025-08-20 12:34:39 -07:00
topjohnwu
b4287700d5 Increase timeout to 15 minutes 2025-08-20 11:23:18 -07:00
topjohnwu
8d10ab89f2 Set zygisk properties in Rust 2025-08-20 11:23:18 -07:00
topjohnwu
49fdc1addb Prevent setting zygisk prop twice 2025-08-20 11:23:18 -07:00
topjohnwu
1333d3b986 Fix canary emulator 2025-08-18 11:25:47 -07:00
残页
335146a6a2 Update supported API levels 2025-08-17 23:58:43 -07:00
topjohnwu
eaf9527971 Use AOSP ATD for API 36
[skip ci]
2025-08-15 17:25:41 -07:00
LoveSy
da937a88c8 if !restore { set_zygisk_prop(); } 2025-08-15 16:45:01 -07:00
topjohnwu
9476e7282d More borrowing, less copying 2025-08-08 21:06:41 -07:00
topjohnwu
251c3c3e0e Remove old ffi data structure 2025-08-08 21:06:41 -07:00
topjohnwu
cd0eca20b0 Migrate connect.cpp to Rust 2025-08-08 21:06:41 -07:00
topjohnwu
6839cb9ab2 Keep /system/xbin/su on emulators 2025-08-08 21:06:41 -07:00
topjohnwu
d11a3397d8 Reduce verbose logging in Zygisk 2025-08-08 21:06:41 -07:00
topjohnwu
975120d6a6 Release Magisk v30.2
[skip ci]
2025-08-06 03:32:32 -07:00
topjohnwu
e489b3b6dd Migrate load_modules to Rust 2025-08-05 11:24:55 -07:00
topjohnwu
589a270b8d Migrate disable/remove modules to Rust 2025-08-05 11:24:55 -07:00
topjohnwu
7961be5cfa Migrate prepare_modules to Rust 2025-08-05 11:24:55 -07:00
topjohnwu
959430e030 Fix systemless hosts installation 2025-08-05 09:44:51 -07:00
topjohnwu
2923c8ccd1 Add module upgrade test 2025-08-05 09:44:51 -07:00
topjohnwu
7df4a9d74f Add uninstaller.sh test 2025-08-05 09:44:51 -07:00
topjohnwu
bf4ed295da Update cargo dependencies 2025-08-02 13:43:27 -07:00
topjohnwu
a5fca960dc Update gradle and dependencies 2025-08-02 02:29:14 -07:00
topjohnwu
f99912b9db Update libsystem_properties 2025-07-21 13:47:30 -07:00
5ec1cff
a54bdb54e4 Skip avb 1,0 verify if tail contains avb 2.0 header
This way, magiskboot will not print "unexpected ASN.1 DER tag: expected SEQUENCE, got APPLICATION [1] (primitive)".
2025-07-21 00:51:14 -07:00
topjohnwu
cd9851a1fe Add regression test for #9179 2025-07-18 17:58:29 -07:00
Wang Han
9ca469898c Use worker for replace feature
This fixes https://github.com/topjohnwu/Magisk/issues/9179.
2025-07-18 16:57:20 -07:00
𝗛𝗼𝗹𝗶
0665549473 Update Turkish
Missing parts were filled in and made better
2025-07-14 10:46:05 -07:00
topjohnwu
9d7a14b335 Remove unnecessary return 2025-07-14 10:03:22 -07:00
Wang Han
62e29fee74 Treat bind mount failure same as C++ implementation
This fixes #9139 and #9174.
2025-07-14 00:27:24 -07:00
igor
e472db552b Update portuguese/english translations 2025-07-11 10:52:21 -07:00
topjohnwu
466e4bd4e1 Update cargo dependencies 2025-07-11 02:04:33 -07:00
topjohnwu
4cf525c588 Add Android canary builds into CI tests 2025-07-11 00:16:39 -07:00
topjohnwu
c8aec2510d Restrict sccache cache size 2025-07-11 00:16:39 -07:00
topjohnwu
ccbfe0e66e Update gradle dependencies 2025-07-10 15:55:14 -07:00
南宫雪珊
23ea28de6f scripts: fix modules_update dir context 2025-07-10 10:59:39 -07:00
topjohnwu
55c3ee3a6f Move Zygisk code out of module.cpp 2025-07-07 13:43:11 -07:00
vvb2060
2a42ca2b8f app: fix time i18n 2025-07-07 11:04:18 -07:00
topjohnwu
a897e82fa4 Remove release notes
They are embedded into GitHub releases
2025-07-07 10:37:45 -07:00
topjohnwu
ffa15831d3 Add release dates 2025-07-07 10:35:22 -07:00
topjohnwu
a344ebf28c Add v30.1 changelog 2025-07-03 18:02:27 -07:00
topjohnwu
78f7fa348e Release Magisk v30.1
[skip ci]
2025-07-03 03:10:10 -07:00
pndwal
d8c448b99d Update faq.md
Add information on restoring Magisk App functionality when stub and full apps conflict.
2025-07-03 02:51:01 -07:00
topjohnwu
d4b83b6a44 Fix app compilation 2025-07-03 02:42:08 -07:00
vvb2060
e5d36d1d24 app: support config restrict policy 2025-07-03 02:42:08 -07:00
vvb2060
ff18cb8e70 su: support drop capabilities 2025-07-03 02:42:08 -07:00
topjohnwu
37a9724a54 Apply clippy fix 2025-07-02 21:20:14 -07:00
topjohnwu
d660401063 Treat magisk symlinks differently 2025-07-02 21:20:14 -07:00
topjohnwu
88541d6f49 Fix file attribute copy in module mounting logic
Due to various reasons, we cannot directly mount module files in /data
into the real paths. Instead we bind mount the module root directory and
remount this mirror with specific mount-point flags. Relevant to this
bug, the module mount is mounted as read-only, which means the file
attribute copy operation could fail in certain configurations.

The fix here is to always copy file attributes into writable locations,
so either in the tmpfs worker directory, or in the module directory
under /data.

A new test case is added to make sure this regression will no longer
happen again in the future.

Fix #9139
2025-07-02 19:23:46 -07:00
topjohnwu
ecd6129fe5 Add systemless hosts test 2025-07-02 19:23:46 -07:00
topjohnwu
6dfe9df9e2 Run cargo fmt 2025-07-02 19:23:46 -07:00
138 changed files with 2975 additions and 3052 deletions

View File

@@ -45,7 +45,7 @@ runs:
env:
SCCACHE_DIRECT: false
SCCACHE_DIR: ${{ github.workspace }}/.sccache
SCCACHE_CACHE_SIZE: 2G
SCCACHE_CACHE_SIZE: ${{ inputs.is-asset-build == 'true' && '2G' || '300M' }}
SCCACHE_IDLE_TIMEOUT: 0
run: |
bash $GITHUB_ACTION_PATH/sccache.sh

View File

@@ -82,12 +82,10 @@ jobs:
strategy:
fail-fast: false
matrix:
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, "36.0-CANARY"]
type: [""]
include:
- version: 36
type: "google_apis"
- version: 36
- version: "36.0-CANARY"
type: "google_apis_ps16k"
steps:
@@ -107,7 +105,7 @@ jobs:
sudo udevadm trigger --name-match=kvm
- name: Run AVD test
timeout-minutes: 10
timeout-minutes: 15
env:
AVD_TEST_LOG: 1
run: scripts/avd.sh test ${{ matrix.version }} ${{ matrix.type }}
@@ -148,7 +146,7 @@ jobs:
sudo udevadm trigger --name-match=kvm
- name: Run AVD test
timeout-minutes: 10
timeout-minutes: 15
env:
FORCE_32_BIT: 1
AVD_TEST_LOG: 1
@@ -193,7 +191,7 @@ jobs:
scripts/cuttlefish.sh download ${{ matrix.branch }} ${{ matrix.device }}
- name: Run Cuttlefish test
timeout-minutes: 10
timeout-minutes: 15
run: sudo -E -u $USER scripts/cuttlefish.sh test
- name: Upload logs on error

View File

@@ -24,6 +24,7 @@ import androidx.core.widget.ImageViewCompat
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener
import androidx.databinding.InverseMethod
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager
@@ -33,9 +34,11 @@ import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.Chip
import com.google.android.material.slider.Slider
import com.google.android.material.textfield.TextInputLayout
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.utils.TextHolder
import com.topjohnwu.superuser.internal.UiThreadHandler
import com.topjohnwu.widget.IndeterminateCheckBox
@@ -306,3 +309,38 @@ fun TextView.setText(text: TextHolder) {
fun Spinner.setAdapter(items: Array<Any>, layoutRes: Int) {
adapter = ArrayAdapter(context, layoutRes, items)
}
@BindingAdapter("labelFormatter")
fun Slider.setLabelFormatter(formatter: (Float) -> Int) {
setLabelFormatter { value -> resources.getString(formatter(value)) }
}
@InverseBindingAdapter(attribute = "android:value")
fun Slider.getValueBinding() = value
@BindingAdapter("android:valueAttrChanged")
fun Slider.setListener(attrChange: InverseBindingListener) {
addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
override fun onStartTrackingTouch(slider: Slider) = Unit
override fun onStopTrackingTouch(slider: Slider) = attrChange.onChange()
})
}
@InverseMethod("sliderValueToPolicy")
fun policyToSliderValue(policy: Int): Float {
return when (policy) {
SuPolicy.DENY -> 1f
SuPolicy.RESTRICT -> 2f
SuPolicy.ALLOW -> 3f
else -> 1f
}
}
fun sliderValueToPolicy(value: Float): Int {
return when (value) {
1f -> SuPolicy.DENY
2f -> SuPolicy.RESTRICT
3f -> SuPolicy.ALLOW
else -> SuPolicy.DENY
}
}

View File

@@ -322,6 +322,12 @@ object Reauthenticate : BaseSettingsItem.Toggle() {
override var value by Config::suReAuth
override fun refresh() {
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Info.showSuperUser
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O
}
}
object Restrict : BaseSettingsItem.Toggle() {
override val title = CoreR.string.settings_su_restrict_title.asText()
override val description = CoreR.string.settings_su_restrict_summary.asText()
override var value by Config::suRestrict
}

View File

@@ -22,11 +22,11 @@ import com.topjohnwu.magisk.core.ktx.activity
import com.topjohnwu.magisk.core.ktx.toast
import com.topjohnwu.magisk.core.tasks.AppMigration
import com.topjohnwu.magisk.core.utils.LocaleSetting
import com.topjohnwu.magisk.core.utils.RootUtils
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.events.AddHomeIconEvent
import com.topjohnwu.magisk.events.AuthEvent
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch
class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
@@ -83,6 +83,9 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
// Can hide overlay windows on 12.0+
list.remove(Tapjack)
}
if (Const.Version.atLeast_30_1()) {
list.add(Restrict)
}
}
return list
@@ -127,7 +130,8 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
}
private fun createHosts() {
Shell.cmd("add_hosts_module").submit {
viewModelScope.launch {
RootUtils.addSystemlessHosts()
AppContext.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT)
}
}

View File

@@ -4,11 +4,13 @@ import android.graphics.drawable.Drawable
import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.databinding.DiffItem
import com.topjohnwu.magisk.databinding.ItemWrapper
import com.topjohnwu.magisk.databinding.ObservableRvItem
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.core.R as CoreR
class PolicyRvItem(
private val viewModel: SuperuserViewModel,
@@ -33,14 +35,34 @@ class PolicyRvItem(
var isExpanded = false
set(value) = set(value, field, { field = it }, BR.expanded)
val showSlider = Config.suRestrict || item.policy == SuPolicy.RESTRICT
@get:Bindable
var isEnabled
get() = item.policy == SuPolicy.ALLOW
get() = item.policy >= SuPolicy.ALLOW
set(value) = setImpl(value, isEnabled) {
notifyPropertyChanged(BR.enabled)
viewModel.togglePolicy(this, value)
viewModel.updatePolicy(this, if (it) SuPolicy.ALLOW else SuPolicy.DENY)
}
@get:Bindable
var sliderValue
get() = item.policy
set(value) = setImpl(value, sliderValue) {
notifyPropertyChanged(BR.sliderValue)
notifyPropertyChanged(BR.enabled)
viewModel.updatePolicy(this, it)
}
val sliderValueToPolicyString: (Float) -> Int = { value ->
when (value.toInt()) {
1 -> CoreR.string.deny
2 -> CoreR.string.restrict
3 -> CoreR.string.grant
else -> CoreR.string.deny
}
}
@get:Bindable
var shouldNotify
get() = item.notification

View File

@@ -156,15 +156,16 @@ class SuperuserViewModel(
}
}
fun togglePolicy(item: PolicyRvItem, enable: Boolean) {
fun updatePolicy(item: PolicyRvItem, policy: Int) {
val items = itemsPolicies.filter { it.item.uid == item.item.uid }
fun updateState() {
viewModelScope.launch {
val res = if (enable) R.string.su_snack_grant else R.string.su_snack_deny
item.item.policy = if (enable) SuPolicy.ALLOW else SuPolicy.DENY
val res = if (policy >= SuPolicy.ALLOW) R.string.su_snack_grant else R.string.su_snack_deny
item.item.policy = policy
db.update(item.item)
items.forEach {
it.notifyPropertyChanged(BR.enabled)
it.notifyPropertyChanged(BR.sliderValue)
}
SnackbarEvent(res.asText(item.appName)).publish()
}

View File

@@ -25,7 +25,7 @@
<include
android:id="@+id/log_track_container"
bullet="@{item.log.action == 2 ? R.drawable.ic_check_md2 : R.drawable.ic_close_md2}"
bullet="@{item.log.action >= 2 ? R.drawable.ic_check_md2 : R.drawable.ic_close_md2}"
isBottom="@{item.isBottom}"
isSelected="@{item.log.action != 2}"
isTop="@{item.isTop}"

View File

@@ -5,6 +5,8 @@
<data>
<import type="com.topjohnwu.magisk.databinding.DataBindingAdaptersKt" />
<variable
name="item"
type="com.topjohnwu.magisk.ui.superuser.PolicyRvItem" />
@@ -85,16 +87,32 @@
app:layout_constraintVertical_bias="0"
tools:text="com.topjohnwu.magisk" />
<com.google.android.material.switchmaterial.SwitchMaterial
<FrameLayout
android:id="@+id/policy_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/l1"
android:checked="@={item.enabled}"
android:nextFocusLeft="@id/policy"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.switchmaterial.SwitchMaterial
gone="@{item.showSlider}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={item.enabled}" />
<com.google.android.material.slider.Slider
goneUnless="@{item.showSlider}"
labelFormatter="@{item.sliderValueToPolicyString}"
android:layout_width="96dp"
android:layout_height="wrap_content"
android:stepSize="1"
android:value="@={DataBindingAdaptersKt.policyToSliderValue(item.sliderValue)}"
android:valueFrom="1"
android:valueTo="3" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -18,12 +18,6 @@ gradlePlugin {
}
}
kotlin {
compilerOptions {
languageVersion = KotlinVersion.KOTLIN_2_0
}
}
dependencies {
implementation(kotlin("gradle-plugin", libs.versions.kotlin.get()))
implementation(libs.android.gradle.plugin)

View File

@@ -309,9 +309,9 @@ fun Project.setupStubApk() {
outputs.dir(outResDir)
doLast {
val apkTmp = File("${apk}.tmp")
exec {
providers.exec {
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
}
}.result.get()
val bos = ByteArrayOutputStream()
ZipFile(apkTmp).use { src ->

View File

@@ -27,10 +27,15 @@ android {
aidl = true
buildConfig = true
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
api(project(":shared"))
coreLibraryDesugaring(libs.jdk.libs)
api(libs.timber)
api(libs.markwon.core)

View File

@@ -38,3 +38,4 @@
-allowaccessmodification
-dontwarn org.junit.**
-dontwarn org.apache.**

View File

@@ -6,4 +6,5 @@ package com.topjohnwu.magisk.core.utils;
interface IRootUtils {
android.app.ActivityManager.RunningAppProcessInfo getAppProcess(int pid);
IBinder getFileSystem();
boolean addSystemlessHosts();
}

View File

@@ -32,6 +32,7 @@ object Config : PreferenceConfig, DBConfig {
const val SU_NOTIFICATION = "su_notification"
const val SU_REAUTH = "su_reauth"
const val SU_TAPJACK = "su_tapjack"
const val SU_RESTRICT = "su_restrict"
const val CHECK_UPDATES = "check_update"
const val RELEASE_CHANNEL = "release_channel"
const val CUSTOM_CHANNEL = "custom_channel"
@@ -147,6 +148,7 @@ object Config : PreferenceConfig, DBConfig {
}
var suReAuth by preference(Key.SU_REAUTH, false)
var suTapjack by preference(Key.SU_TAPJACK, true)
var suRestrict by preference(Key.SU_RESTRICT, false)
private const val SU_FINGERPRINT = "su_fingerprint"
private const val UPDATE_CHANNEL = "update_channel"

View File

@@ -26,9 +26,11 @@ object Const {
const val MIN_VERSION = "v22.0"
const val MIN_VERCODE = 22000
fun atLeast_24_0() = Info.env.versionCode >= 24000
fun atLeast_25_0() = Info.env.versionCode >= 25000
fun atLeast_28_0() = Info.env.versionCode >= 28000
private fun isCanary() = (Info.env.versionCode % 100) != 0
fun atLeast_24_0() = Info.env.versionCode >= 24000 || isCanary()
fun atLeast_25_0() = Info.env.versionCode >= 25000 || isCanary()
fun atLeast_28_0() = Info.env.versionCode >= 28000 || isCanary()
fun atLeast_30_1() = Info.env.versionCode >= 30100 || isCanary()
}
object ID {

View File

@@ -109,7 +109,7 @@ fun PackageManager.getPackageInfo(uid: Int, pid: Int): PackageInfo? {
return null
}
// Try to find package name from PID
val proc = RootUtils.obj?.getAppProcess(pid)
val proc = RootUtils.getAppProcess(pid)
if (proc == null) {
if (uid == Process.SHELL_UID) {
// It is possible that some apps installed are sharing UID with shell.

View File

@@ -14,10 +14,11 @@ import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.lang.reflect.Field
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.Collections
import java.util.Locale
inline fun <In : Closeable, Out : Closeable> withInOut(
input: In,
@@ -83,19 +84,15 @@ inline fun <T, R> Flow<T>.concurrentMap(crossinline transform: suspend (T) -> R)
}
}
fun Long.toTime(format: DateFormat) = format.format(this).orEmpty()
fun Long.toTime(format: DateTimeFormatter): String = format.format(Instant.ofEpochMilli(this))
// Some devices don't allow filenames containing ":"
val timeFormatStandard by lazy {
SimpleDateFormat(
"yyyy-MM-dd'T'HH.mm.ss",
Locale.ROOT
)
val timeFormatStandard: DateTimeFormatter by lazy {
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH.mm.ss").withZone(ZoneId.systemDefault())
}
val timeDateFormat: DateFormat by lazy {
DateFormat.getDateTimeInstance(
DateFormat.DEFAULT,
DateFormat.DEFAULT,
Locale.ROOT
)
val timeDateFormat: DateTimeFormatter by lazy {
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withZone(ZoneId.systemDefault())
}
val dateFormat: DateTimeFormatter by lazy {
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withZone(ZoneId.systemDefault())
}

View File

@@ -7,8 +7,7 @@ import com.squareup.moshi.JsonClass
import com.squareup.moshi.JsonQualifier
import com.squareup.moshi.ToJson
import kotlinx.parcelize.Parcelize
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME
import java.time.Instant
@JsonClass(generateAdapter = true)
class UpdateJson(
@@ -40,13 +39,13 @@ data class ReleaseAssets(
class DateTimeAdapter {
@ToJson
fun toJson(date: LocalDateTime): String {
fun toJson(date: Instant): String {
return date.toString()
}
@FromJson
fun fromJson(date: String): LocalDateTime {
return LocalDateTime.parse(date, ISO_OFFSET_DATE_TIME)
fun fromJson(date: String): Instant {
return Instant.parse(date)
}
}
@@ -57,7 +56,7 @@ data class Release(
val prerelease: Boolean,
val assets: List<ReleaseAssets>,
val body: String,
@Json(name = "created_at") val createdTime: LocalDateTime,
@Json(name = "created_at") val createdTime: Instant,
) {
val versionCode: Int get() {
return if (tag[0] == 'v') {

View File

@@ -13,7 +13,7 @@ import java.io.IOException
import java.util.Locale
data class LocalModule(
private val base: ExtendedFile,
val base: ExtendedFile,
) : Module() {
private val svc get() = ServiceLocator.networkService

View File

@@ -4,15 +4,16 @@ import com.topjohnwu.magisk.core.data.magiskdb.MagiskDB
class SuPolicy(
val uid: Int,
var policy: Int = INTERACTIVE,
var policy: Int = QUERY,
var remain: Long = -1L,
var logging: Boolean = true,
var notification: Boolean = true,
) {
companion object {
const val INTERACTIVE = 0
const val QUERY = 0
const val DENY = 1
const val ALLOW = 2
const val RESTRICT = 3
}
fun toMap(): MutableMap<String, Any> {

View File

@@ -10,13 +10,13 @@ import com.topjohnwu.magisk.core.Config.Value.STABLE_CHANNEL
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.data.GithubApiServices
import com.topjohnwu.magisk.core.data.RawUrl
import com.topjohnwu.magisk.core.ktx.dateFormat
import com.topjohnwu.magisk.core.model.Release
import com.topjohnwu.magisk.core.model.ReleaseAssets
import com.topjohnwu.magisk.core.model.UpdateInfo
import retrofit2.HttpException
import timber.log.Timber
import java.io.IOException
import java.time.format.DateTimeFormatter
class NetworkService(
private val raw: RawUrl,
@@ -74,7 +74,7 @@ class NetworkService(
private inline fun Release.asPublicInfo(selector: (ReleaseAssets) -> Boolean): UpdateInfo {
val version = tag.drop(1)
val date = createdTime.format(DateTimeFormatter.ofPattern("yyyy.M.d"))
val date = dateFormat.format(createdTime)
return UpdateInfo(
version = version,
versionCode = versionCode,

View File

@@ -70,7 +70,7 @@ object SuCallbackHandler {
}.getOrNull() ?: createSuLog(fromUid, toUid, pid, command, policy, target, seContext, gids)
if (notify)
notify(context, log.action == SuPolicy.ALLOW, log.appName)
notify(context, log.action >= SuPolicy.ALLOW, log.appName)
runBlocking { ServiceLocator.logRepo.insert(log) }
}
@@ -86,7 +86,7 @@ object SuCallbackHandler {
pm.getPackageInfo(uid, pid)?.applicationInfo?.getLabel(pm)
}.getOrNull() ?: "[UID] $uid"
notify(context, policy == SuPolicy.ALLOW, appName)
notify(context, policy >= SuPolicy.ALLOW, appName)
}
private fun notify(context: Context, granted: Boolean, appName: String) {

View File

@@ -82,7 +82,11 @@ class SuRequestHandler(
}
suspend fun respond(action: Int, time: Long) {
policy.policy = action
if (action == SuPolicy.ALLOW && Config.suRestrict) {
policy.policy = SuPolicy.RESTRICT
} else {
policy.policy = action
}
if (time >= 0) {
policy.remain = TimeUnit.MINUTES.toSeconds(time)
} else {

View File

@@ -7,11 +7,14 @@ import android.content.ServiceConnection
import android.os.IBinder
import android.system.Os
import androidx.core.content.getSystemService
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.ipc.RootService
import com.topjohnwu.superuser.nio.FileSystemManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.File
import java.util.concurrent.locks.AbstractQueuedSynchronizer
@@ -43,16 +46,7 @@ class RootUtils(stub: Any?) : RootService() {
return object : IRootUtils.Stub() {
override fun getAppProcess(pid: Int) = safe(null) { getAppProcessImpl(pid) }
override fun getFileSystem(): IBinder = FileSystemManager.getService()
}
}
private inline fun <T> safe(default: T, block: () -> T): T {
return try {
block()
} catch (e: Throwable) {
// The process died unexpectedly
Timber.e(e)
default
override fun addSystemlessHosts() = safe(false) { addSystemlessHostsImpl() }
}
}
@@ -78,6 +72,26 @@ class RootUtils(stub: Any?) : RootService() {
return null
}
private fun addSystemlessHostsImpl(): Boolean {
val module = File(Const.MODULE_PATH, "hosts")
if (module.exists()) return true
val hosts = File(module, "system/etc/hosts")
if (!hosts.parentFile.mkdirs()) return false
File(module, "module.prop").outputStream().writer().use {
it.write("""
id=hosts
name=Systemless Hosts
version=1.0
versionCode=1
author=Magisk
description=Magisk app built-in systemless hosts module
""".trimIndent())
}
File("/system/etc/hosts").copyTo(hosts)
File(module, "update").createNewFile()
return true
}
object Connection : AbstractQueuedSynchronizer(), ServiceConnection {
init {
state = 1
@@ -131,11 +145,25 @@ class RootUtils(stub: Any?) : RootService() {
return field
}
private set
var obj: IRootUtils? = null
private var obj: IRootUtils? = null
get() {
Connection.await()
return field
}
private set
fun getAppProcess(pid: Int) = safe(null) { obj?.getAppProcess(pid) }
suspend fun addSystemlessHosts() =
withContext(Dispatchers.IO) { safe(false) { obj?.addSystemlessHosts() ?: false } }
private inline fun <T> safe(default: T, block: () -> T): T {
return try {
block()
} catch (e: Throwable) {
// The process died unexpectedly
Timber.e(e)
default
}
}
}
}

View File

@@ -12,9 +12,11 @@ import com.topjohnwu.magisk.test.Environment.Companion.INVALID_ZYGISK
import com.topjohnwu.magisk.test.Environment.Companion.MOUNT_TEST
import com.topjohnwu.magisk.test.Environment.Companion.REMOVE_TEST
import com.topjohnwu.magisk.test.Environment.Companion.SEPOLICY_RULE
import com.topjohnwu.magisk.test.Environment.Companion.UPGRADE_TEST
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@@ -55,7 +57,7 @@ class AdditionalTest : BaseTest {
@Test
fun testModuleCount() {
var expected = 2
var expected = 4
if (Environment.mount()) expected++
if (Environment.preinit()) expected++
if (Environment.lsposed()) expected++
@@ -90,17 +92,18 @@ class AdditionalTest : BaseTest {
assertNotNull("$MOUNT_TEST is not installed", modules.find { it.id == MOUNT_TEST })
assertTrue(
"/system/etc/newfile should exist",
RootUtils.fs.getFile("/system/etc/newfile").exists()
"/system/fonts/newfile should exist",
RootUtils.fs.getFile("/system/fonts/newfile").exists()
)
assertFalse(
"/system/bin/screenrecord should not exist",
RootUtils.fs.getFile("/system/bin/screenrecord").exists()
)
val egg = RootUtils.fs.getFile("/system/app/EasterEgg").list() ?: arrayOf()
assertTrue(
"/system/app/EasterEgg should be empty",
egg.isEmpty()
assertArrayEquals(
"/system/app/EasterEgg should be replaced",
egg,
arrayOf("newfile")
)
}
@@ -134,5 +137,25 @@ class AdditionalTest : BaseTest {
@Test
fun testRemoveModule() {
assertNull("$REMOVE_TEST should be removed", modules.find { it.id == REMOVE_TEST })
assertTrue(
"Uninstaller of $REMOVE_TEST should be run",
RootUtils.fs.getFile(Environment.REMOVE_TEST_MARKER).exists()
)
}
@Test
fun testModuleUpgrade() {
val module = modules.find { it.id == UPGRADE_TEST }
assertNotNull("$UPGRADE_TEST is not installed", module)
module!!
assertFalse("$UPGRADE_TEST should be disabled", module.enable)
assertTrue(
"$UPGRADE_TEST should be updated",
module.base.getChildFile("post-fs-data.sh").exists()
)
assertFalse(
"$UPGRADE_TEST should be updated",
module.base.getChildFile("service.sh").exists()
)
}
}

View File

@@ -58,12 +58,15 @@ class Environment : BaseTest {
return Build.VERSION.SDK_INT >= 27
}
private const val MODULE_UPDATE_PATH = "/data/adb/modules_update"
private const val MODULE_ERROR = "Module zip processing incorrect"
const val MOUNT_TEST = "mount_test"
const val SEPOLICY_RULE = "sepolicy_rule"
const val INVALID_ZYGISK = "invalid_zygisk"
const val REMOVE_TEST = "remove_test"
const val REMOVE_TEST_MARKER = "/dev/.remove_test_removed"
const val EMPTY_ZYGISK = "empty_zygisk"
const val UPGRADE_TEST = "upgrade_test"
}
object TimberLog : CallbackList<String>(Runnable::run) {
@@ -98,8 +101,8 @@ class Environment : BaseTest {
val error = "$MOUNT_TEST setup failed"
val path = root.getChildFile(MOUNT_TEST)
// Create /system/etc/newfile
val etc = path.getChildFile("system").getChildFile("etc")
// Create /system/fonts/newfile
val etc = path.getChildFile("system").getChildFile("fonts")
assertTrue(error, etc.mkdirs())
assertTrue(error, etc.getChildFile("newfile").createNewFile())
@@ -108,6 +111,9 @@ class Environment : BaseTest {
assertTrue(error, egg.mkdirs())
assertTrue(error, egg.getChildFile(".replace").createNewFile())
// Create /system/app/EasterEgg/newfile
assertTrue(error, egg.getChildFile("newfile").createNewFile())
// Delete /system/bin/screenrecord
val bin = path.getChildFile("system").getChildFile("bin")
assertTrue(error, bin.mkdirs())
@@ -116,6 +122,12 @@ class Environment : BaseTest {
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
}
private fun setupSystemlessHost() {
val error = "hosts setup failed"
assertTrue(error, runBlocking { RootUtils.addSystemlessHosts() })
assertTrue(error, RootUtils.fs.getFile(Const.MODULE_PATH).getChildFile("hosts").exists())
}
private fun setupSepolicyRuleModule(root: ExtendedFile) {
val error = "$SEPOLICY_RULE setup failed"
val path = root.getChildFile(SEPOLICY_RULE)
@@ -163,12 +175,39 @@ class Environment : BaseTest {
// Create a new module but mark is as "remove"
val module = LocalModule(path)
assertTrue(error, path.mkdirs())
// Create uninstaller script
path.getChildFile("uninstall.sh").newOutputStream().writer().use {
it.write("touch $REMOVE_TEST_MARKER")
}
assertTrue(error, path.getChildFile("service.sh").createNewFile())
module.remove = true
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
}
private fun setupUpgradeModule(root: ExtendedFile, update: ExtendedFile) {
val error = "$UPGRADE_TEST setup failed"
val oldPath = root.getChildFile(UPGRADE_TEST)
val newPath = update.getChildFile(UPGRADE_TEST)
// Create an existing module but mark as "disable
val module = LocalModule(oldPath)
assertTrue(error, oldPath.mkdirs())
module.enable = false
// Install service.sh into the old module
assertTrue(error, oldPath.getChildFile("service.sh").createNewFile())
// Create an upgrade module
assertTrue(error, newPath.mkdirs())
// Install post-fs-data.sh into the new module
assertTrue(error, newPath.getChildFile("post-fs-data.sh").createNewFile())
assertTrue(error, Shell.cmd(
"set_default_perm $oldPath",
"set_default_perm $newPath",
).exec().isSuccess)
}
@Test
fun setupEnvironment() {
runBlocking {
@@ -213,11 +252,14 @@ class Environment : BaseTest {
}
val root = RootUtils.fs.getFile(Const.MODULE_PATH)
if (mount()) { setupMountTest(root) }
if (preinit()) { setupSepolicyRuleModule(root) }
setupEmptyZygiskModule(root)
setupInvalidZygiskModule(root)
val update = RootUtils.fs.getFile(MODULE_UPDATE_PATH)
if (mount()) { setupMountTest(update) }
if (preinit()) { setupSepolicyRuleModule(update) }
setupSystemlessHost()
setupEmptyZygiskModule(update)
setupInvalidZygiskModule(update)
setupRemoveModule(root)
setupUpgradeModule(root, update)
}
@Test

View File

@@ -53,6 +53,7 @@
<string name="touch_filtered_warning">Como um app está ocultando uma solicitação de SuperUsuário, o Magisk não pode verificar sua resposta.</string>
<string name="deny">Negar</string>
<string name="prompt">Perguntar</string>
<string name="restrict">Restringir</string>
<string name="grant">Permitir</string>
<string name="su_warning">Permite acesso total ao seu dispositivo.\nNão permita se você não tiver certeza do que está fazendo!</string>
<string name="forever">Sempre</string>
@@ -170,6 +171,8 @@
<string name="settings_su_auth_title">Autenticação de usuário</string>
<string name="settings_su_auth_summary">Solicite autenticação de usuário durante solicitações de SuperUsuário</string>
<string name="settings_su_auth_insecure">Nenhum método de autenticação está configurado no dispositivo</string>
<string name="settings_su_restrict_title">Restringir recursos root</string>
<string name="settings_su_restrict_summary">Restringirá novos apps de SuperUsuário por padrão. Aviso: isso quebrará a maioria dos apps. Não ative se você não souber o que está fazendo.</string>
<string name="settings_customization">Personalizações</string>
<string name="setting_add_shortcut_summary">Adicione um atalho na tela inicial, caso o nome e o ícone sejam difíceis de reconhecer logo após ocultar o app.</string>
<string name="settings_doh_title">DNS sobre HTTPS</string>

View File

@@ -53,6 +53,7 @@
<string name="touch_filtered_warning">Como um app está a ocultar um pedido de SuperUsuário, o Magisk não consegue verificar a sua resposta.</string>
<string name="deny">Negar</string>
<string name="prompt">Perguntar</string>
<string name="restrict">Restringir</string>
<string name="grant">Permitir</string>
<string name="su_warning">Permite o acesso total ao seu dispositivo.\nNão o permita se não tiver a certeza do que está a fazer!</string>
<string name="forever">Sempre</string>
@@ -129,7 +130,7 @@
<string name="settings_restore_app_title">Restaurar app do Magisk</string>
<string name="settings_restore_app_summary">Desoculta o app do Magisk e restaura o APK original</string>
<string name="language">Idioma</string>
<string name="system_default">(Padrão do sistema)</string>
<string name="system_default">(Predefinição do sistema)</string>
<string name="settings_check_update_title">Verificar por atualizações</string>
<string name="settings_check_update_summary">Verifique automaticamente se há atualizações ao abrir o app</string>
<string name="settings_update_channel_title">Canal de atualização</string>
@@ -170,6 +171,8 @@
<string name="settings_su_auth_title">Autenticação de usuário</string>
<string name="settings_su_auth_summary">Solicite autenticação de usuário durante pedidos de SuperUsuário</string>
<string name="settings_su_auth_insecure">Nenhum método de autenticação está configurado no dispositivo</string>
<string name="settings_su_restrict_title">Restringir recursos root</string>
<string name="settings_su_restrict_summary">Restringirá novos apps de SuperUsuário por predefinição. Aviso: isto quebrará a maioria dos apps. Não ative se você não souber o que está a fazer.</string>
<string name="settings_customization">Personalizações</string>
<string name="setting_add_shortcut_summary">Adicione um atalho no ecrã inicial, caso o nome e o ícone sejam difíceis de reconhecer logo após ocultar o app.</string>
<string name="settings_doh_title">DNS sobre HTTPS</string>

View File

@@ -5,116 +5,114 @@
<string name="superuser">Süper Kullanıcı</string>
<string name="logs">Günlükler</string>
<string name="settings">Ayarlar</string>
<string name="install">Yükle</string>
<string name="install">Kur</string>
<string name="section_home">Ana Sayfa</string>
<string name="section_theme">Temalar</string>
<string name="denylist">Reddetme Listesi</string>
<string name="denylist">Red Listesi</string>
<!--Home-->
<string name="no_connection">Bağlantı yok</string>
<string name="no_connection">Bağlantı Yok</string>
<string name="app_changelog">Değişiklik Günlüğü</string>
<string name="loading">Yükleniyor…</string>
<string name="update">Güncelle</string>
<string name="not_available">Yok</string>
<string name="not_available">Mevcut Değil</string>
<string name="hide">Gizle</string>
<string name="home_package">Paket</string>
<string name="home_app_title">Uygulama</string>
<string name="home_notice_content">Magisk\'i YALNIZCA resmi GitHub sayfasından indirin. Bilinmeyen kaynaklardan gelen dosyalar zararlı olabilir!</string>
<string name="home_support_title">Bizi Destekleyin</string>
<string name="home_notice_content">Magisk\'i YALNIZCA resmi GitHub sayfasından indirin. Bilinmeyen kaynaklardan gelen dosyalar kötü amaçlı olabilir!</string>
<string name="home_support_title">Bize Destek Olun</string>
<string name="home_follow_title">Bizi Takip Edin</string>
<string name="home_item_source">Kaynak</string>
<string name="home_support_content">Magisk her zaman ücretsiz veık kaynak olacaktır. Ancak, bir bağış yaparak bize destek olabilirsiniz.</string>
<string name="home_installed_version">Yüklü Sürüm</string>
<string name="home_latest_version">En Son Sürüm</string>
<string name="invalid_update_channel">Geçersiz Güncelleme Kanalı</string>
<string name="home_support_content">Magisk ücretsizdir,ık kaynaklıdır ve her zaman öyle kalacaktır. Ancak, bağış yaparak bize değer verdiğinizi gösterebilirsiniz.</string>
<string name="home_installed_version">Yüklü</string>
<string name="home_latest_version">En Son</string>
<string name="invalid_update_channel">Geçersiz güncelleme kanalı</string>
<string name="uninstall_magisk_title">Magisk\'i Kaldır</string>
<string name="uninstall_magisk_msg">Tüm modüller devre dışı bırakılacak/kaldırılacak!\nKök kaldırılacak!\nMagisk kullanılarak şifrelenmemiş herhangi bir dahili depolama yeniden şifrelenecek!</string>
<string name="uninstall_magisk_msg">Tüm modüller devre dışı bırakılacak/kaldırılacak!\nRoot kaldırılacak!\nMagisk kullanılarak şifresi çözülen dahili depolama birimleri yeniden şifrelenecek!</string>
<!--Install-->
<string name="keep_force_encryption">Zorla şifrelemeyi koru</string>
<string name="keep_dm_verity">AVB 2.0/dm-verity\'yi koru</string>
<string name="keep_force_encryption">Zorunlu Şifrelemeyi Koru</string>
<string name="keep_dm_verity">AVB 2.0/dm-verity Koru</string>
<string name="recovery_mode">Kurtarma Modu</string>
<string name="install_options_title">Seçenekler</string>
<string name="install_method_title">Yöntem</string>
<string name="install_next">Sonraki</string>
<string name="install_start">Hadi başlayalım</string>
<string name="manager_download_install">İndirmek ve yüklemek için basın</string>
<string name="direct_install">Doğrudan Yükleme (Önerilir)</string>
<string name="install_inactive_slot">Etkin Olmayan Slot\'a Yükle (OTA Sonrası)</string>
<string name="install_inactive_slot_msg">Cihazınız yeniden başlatıldıktan sonra zorunlu olarak mevcut etkin olmayan slota önyükleme yapılacaktır!\nBu seçeneği yalnızca OTA tamamlandıktan sonra kullanın.\nDevam etmek istiyor musunuz?</string>
<string name="install_start">Hadi Başlayalım</string>
<string name="manager_download_install">İndirmek ve kurmak için basın</string>
<string name="direct_install">Doğrudan kurulum (Önerilen)</string>
<string name="install_inactive_slot">Etkin olmayan slota kur (OTA sonrası)</string>
<string name="install_inactive_slot_msg">Cihazınız, yeniden başlattıktan sonra mevcut etkin olmayan slota önyükleme yapmaya ZORLANACAKTIR!\nBu seçeneği yalnızca OTA güncellemesi yapıldıktan sonra kullanın.\nDevam edilsin mi?</string>
<string name="setup_title">Ek Kurulum</string>
<string name="select_patch_file">Bir Dosya Seç ve Yama Yap</string>
<string name="patch_file_msg">Ham bir görüntü (*.img) veya bir ODIN tar dosyası (*.tar) veya bir payload.bin (*.bin) seçin</string>
<string name="select_patch_file">Bir dosya sin ve yamalayın</string>
<string name="patch_file_msg">Ham bir imaj (*.img), bir ODIN tar dosyası (*.tar) veya bir payload.bin (*.bin) seçin</string>
<string name="reboot_delay_toast">5 saniye içinde yeniden başlatılıyor…</string>
<string name="flash_screen_title">Yükleniyor</string>
<string name="flash_screen_title">Kuruluyor</string>
<!--Superuser-->
<string name="su_request_title">Süper Kullanıcı İsteği</string>
<string name="touch_filtered_warning">Bir uygulama bir Süper Kullanıcı isteğini engellediği için Magisk yanıtınızı doğrulayamıyor</string>
<string name="touch_filtered_warning">Bir uygulama Süper Kullanıcı isteğini engellediği için Magisk yanıtınızı doğrulayamıyor.</string>
<string name="deny">Reddet</string>
<string name="prompt">İstem</string>
<string name="prompt">Sor</string>
<string name="restrict">Kısıtla</string>
<string name="grant">İzin Ver</string>
<string name="su_warning">Cihazınıza tam erişim sağlar.\nEmin değilseniz reddedin!</string>
<string name="forever">Daima</string>
<string name="once">Bir kez</string>
<string name="tenmin">10 dakika</string>
<string name="twentymin">20 dakika</string>
<string name="thirtymin">30 dakika</string>
<string name="sixtymin">60 dakika</string>
<string name="su_allow_toast">%1$s uygulamasının süper kullanıcı hakları verildi</string>
<string name="su_deny_toast">%1$s uygulamasının süper kullanıcı hakları reddedildi</string>
<string name="su_snack_grant">%1$s uygulamasının süper kullanıcı hakları verildi</string>
<string name="su_snack_deny">%1$s uygulamasının süper kullanıcı hakları reddedildi</string>
<string name="su_snack_notif_on">%1$s uygulamasının bildirimleri etkinleştirildi</string>
<string name="su_snack_notif_off">%1$s uygulamasının bildirimleri devre dışı bırakıldı</string>
<string name="su_snack_log_on">%1$s uygulamasının günlüğü etkinleştirildi</string>
<string name="su_snack_log_off">%1$s uygulamasının günlüğü devre dışı bırakıldı</string>
<string name="su_revoke_title">İptal Et?</string>
<string name="su_revoke_msg">%1$s uygulamasının süper kullanıcı haklarını iptal etmek istediğinize emin misiniz?</string>
<string name="toast">Bildirim</string>
<string name="forever">Her Zaman</string>
<string name="once">Bir Kez</string>
<string name="tenmin">10 Dakika</string>
<string name="twentymin">20 Dakika</string>
<string name="thirtymin">30 Dakika</string>
<string name="sixtymin">60 Dakika</string>
<string name="su_allow_toast">%1$s uygulamasına Süper Kullanıcı hakları verildi</string>
<string name="su_deny_toast">%1$s uygulamasının Süper Kullanıcı hakları reddedildi</string>
<string name="su_snack_grant">%1$s uygulamasının Süper Kullanıcı hakları verildi</string>
<string name="su_snack_deny">%1$s uygulamasının Süper Kullanıcı hakları reddedildi</string>
<string name="su_snack_notif_on">%1$s bildirimleri etkinleştirildi</string>
<string name="su_snack_notif_off">%1$s bildirimleri devre dışı bırakıldı</string>
<string name="su_snack_log_on">%1$s için günlük kaydı etkinleştirildi</string>
<string name="su_snack_log_off">%1$s için günlük kaydı devre dışı bırakıldı</string>
<string name="su_revoke_title">İptal Edilsin mi?</string>
<string name="su_revoke_msg">%1$s uygulamasının Süper Kullanıcı haklarını iptal etmeyi onaylayın</string>
<string name="toast">Bildirim Penceresi</string>
<string name="none">Yok</string>
<string name="superuser_toggle_notification">Bildirimler</string>
<string name="superuser_toggle_revoke">İptal Et</string>
<string name="superuser_policy_none">Henüz hiçbir uygulama Süper Kullanıcı izni istemedi.</string>
<!--Logs-->
<string name="log_data_none">Günlük kullanmıyorsunuz, root (kök) uygulamalarınızı daha çok kullanmayı deneyin</string>
<string name="log_data_magisk_none">Magisk günlükleri boş, bu tuhaf</string>
<string name="menuSaveLog">Günlüğü kaydet</string>
<string name="menuClearLog">Günlüğü şimdi temizle</string>
<string name="logs_cleared">Günlük kaydı başarıyla temizlendi</string>
<string name="log_data_none">Günlüğünüz temiz. Root uygulamalarınızı daha fazla kullanmayı deneyin.</string>
<string name="log_data_magisk_none">Magisk günlükleri boş, bu garip.</string>
<string name="menuSaveLog">Günlüğü Kaydet</string>
<string name="menuClearLog">Günlüğü Şimdi Temizle</string>
<string name="logs_cleared">Günlük başarıyla temizlendi</string>
<string name="pid">PID: %1$d</string>
<string name="target_uid">Hedef UID: %1$d</string>
<string name="target_pid">Mount ns hedef PID: %s</string>
<string name="target_pid">Hedef PID: %s</string>
<string name="selinux_context">SELinux bağlamı: %s</string>
<string name="supp_group">Ek grup: %s</string>
<!--SafetyNet-->
<!--MagiskHide-->
<string name="show_system_app">Sistem uygulamalarını göster</string>
<string name="show_os_app">İşletim sistemi uygulamalarını göster</string>
<string name="hide_filter_hint">İsme göre filtrele</string>
<string name="show_system_app">Sistem Uygulamalarını Göster</string>
<string name="show_os_app">İS Uygulamalarını Göster</string>
<string name="hide_filter_hint">Ada göre filtrele</string>
<string name="hide_search">Ara</string>
<!--Module-->
<string name="no_info_provided">(Bilgi verilmedi)</string>
<string name="reboot_userspace">Hızlı yeniden başlat</string>
<string name="reboot_recovery">Kurtarma modunda yeniden başlat</string>
<string name="reboot_bootloader">Önyükleyici modunda yeniden başlat</string>
<string name="reboot_download">İndirme modunda yeniden başlat</string>
<string name="reboot_edl">EDL modunda yeniden başlat</string>
<string name="reboot_safe_mode">Güvenli mod</string>
<string name="module_version_author">%1$s / %2$s</string>
<string name="no_info_provided">(Bilgi sağlanmadı)</string>
<string name="reboot_userspace">Hızlı Yeniden Başlat</string>
<string name="reboot_recovery">Kurtarma Modunda Yeniden Başlat</string>
<string name="reboot_bootloader">Önyükleyici Modunda Yeniden Başlat</string>
<string name="reboot_download">Download Modunda Yeniden Başlat</string>
<string name="reboot_edl">EDL Modunda Yeniden Başlat</string>
<string name="reboot_safe_mode">Güvenli Mod</string>
<string name="module_version_author">%1$s, Geliştirici: %2$s</string>
<string name="module_state_remove">Kaldır</string>
<string name="module_action">Eylem</string>
<string name="module_state_restore">Geri Yükle</string>
<string name="module_action_install_external">Depolamadan yükle</string>
<string name="update_available">Güncelleme Mevcut</string>
<string name="update_available">Güncelleme mevcut</string>
<string name="suspend_text_riru">Modül, %1$s etkin olduğu için askıya alındı</string>
<string name="suspend_text_zygisk">Modül, %1$s etkin olmadığı için askıya alındı</string>
<string name="zygisk_module_unloaded">Uyumsuzluk nedeniyle Zygisk modülü yüklenmedi</string>
<string name="zygisk_module_unloaded">Zygisk modülü uyumsuzluk nedeniyle yüklenmedi</string>
<string name="module_empty">Yüklü modül yok</string>
<string name="confirm_install">%1$s modülü yüklensin mi?</string>
<string name="confirm_install_title">Yükleme Onayı</string>
@@ -127,121 +125,125 @@
<string name="settings_dark_mode_dark">Her Zaman Karanlık</string>
<string name="settings_download_path_title">İndirme Yolu</string>
<string name="settings_download_path_message">Dosyalar %1$s konumuna kaydedilecek</string>
<string name="settings_hide_app_title">Magisk uygulamasını gizle</string>
<string name="settings_hide_app_summary">Rastgele bir paket kimliği ve özel uygulama etiketi olan bir vekil (proxy) uygulaması yükleyin</string>
<string name="settings_restore_app_title">Magisk uygulamasını geri yükle</string>
<string name="settings_restore_app_summary">Uygulamayı göster ve orijinal APK\'yı geri yükle</string>
<string name="settings_hide_app_title">Magisk Uygulamasını Gizle</string>
<string name="settings_hide_app_summary">Rastgele bir paket kimliği ve özel uygulama etiketi ile bir proxy uygulaması yükleyin</string>
<string name="settings_restore_app_title">Magisk Uygulamasını Geri Yükle</string>
<string name="settings_restore_app_summary">Uygulamanın gizliliğini kaldırın ve orijinal APK\'yi geri yükleyin</string>
<string name="language">Dil</string>
<string name="system_default">(Sistem Varsayılanı)</string>
<string name="settings_check_update_title">Güncellemeleri Kontrol Et</string>
<string name="settings_check_update_summary">Arka planda düzenli olarak güncellemeleri kontrol et</string>
<string name="settings_check_update_summary">Arka planda periyodik olarak güncellemeleri kontrol et</string>
<string name="settings_update_channel_title">Güncelleme Kanalı</string>
<string name="settings_update_stable">Kararlı</string>
<string name="settings_update_beta">Beta</string>
<string name="settings_update_debug">Hata Ayıklama</string>
<string name="settings_update_custom">Özel</string>
<string name="settings_update_custom_msg">Özel kanal URL\'si girin</string>
<string name="settings_zygisk_summary">Magisk\'in bazı bölümlerini zygote daemon\'unda çalıştır</string>
<string name="settings_denylist_title">Reddetme Listesini Zorla</string>
<string name="settings_denylist_summary">Reddetme Listesindeki işlemler tüm Magisk değişikliklerini geri alacak</string>
<string name="settings_denylist_config_title">Reddetme Listesini Yapılandır</string>
<string name="settings_denylist_config_summary">Reddetme Listesine dahil edilecek işlemleri seçin</string>
<string name="settings_hosts_title">Sistemsiz ana makineler (systemless hosts)</string>
<string name="settings_hosts_summary">Reklam engelleme uygulamaları için sistemsiz ana makineler (systemless hosts) desteği</string>
<string name="settings_hosts_toast">Sistemsiz ana makineler (systemless hosts) modülü eklendi</string>
<string name="settings_update_custom_msg">Özel bir kanal URL\'si girin</string>
<string name="settings_zygisk_summary">Magisk\'in bazı kısımlarını Zygote daemon içinde çalıştırın</string>
<string name="settings_denylist_title">Red Listesini Uygula</string>
<string name="settings_denylist_summary">Red listesindeki işlemlerin tüm Magisk değişiklikleri geri alınacak</string>
<string name="settings_denylist_config_title">Red Listesini Yapılandır</string>
<string name="settings_denylist_config_summary">Red listesine dahil edilecek işlemleri seçin</string>
<string name="settings_hosts_title">Sistemsiz Hosts</string>
<string name="settings_hosts_summary">Reklam engelleme uygulamaları için sistemsiz hosts desteği</string>
<string name="settings_hosts_toast">Sistemsiz hosts modülü eklendi</string>
<string name="settings_app_name_hint">Yeni ad</string>
<string name="settings_app_name_helper">Uygulama bu isimle yeniden paketlenecek</string>
<string name="settings_app_name_helper">Uygulama bu adla yeniden paketlenecek</string>
<string name="settings_app_name_error">Geçersiz format</string>
<string name="settings_su_app_adb">Uygulamalar ve ADB</string>
<string name="settings_su_app">Sadece Uygulamalar</string>
<string name="settings_su_adb">Sadece ADB</string>
<string name="settings_su_disable">Devre Dışı</string>
<string name="settings_su_request_10">10 saniye</string>
<string name="settings_su_request_15">15 saniye</string>
<string name="settings_su_request_20">20 saniye</string>
<string name="settings_su_request_30">30 saniye</string>
<string name="settings_su_request_45">45 saniye</string>
<string name="settings_su_request_60">60 saniye</string>
<string name="settings_su_request_10">10 Saniye</string>
<string name="settings_su_request_15">15 Saniye</string>
<string name="settings_su_request_20">20 Saniye</string>
<string name="settings_su_request_30">30 Saniye</string>
<string name="settings_su_request_45">45 Saniye</string>
<string name="settings_su_request_60">60 Saniye</string>
<string name="superuser_access">Süper Kullanıcı Erişimi</string>
<string name="auto_response">Otomatik Yanıt</string>
<string name="request_timeout">İstek Zaman Aşımı</string>
<string name="superuser_notification">Süper Kullanıcı Bildirimi</string>
<string name="settings_su_reauth_title">Yükseltme sonrası yeniden doğrulama yap</string>
<string name="settings_su_reauth_summary">Uygulama güncellemelerinden sonra Süper Kullanıcı izinlerini tekrar iste</string>
<string name="settings_su_tapjack_title">Tapjacking Koruması</string>
<string name="settings_su_tapjack_summary">Süper Kullanıcı istemi diyalogu, başka bir pencere veya katman tarafından gizlendiğinde girdilere yanıt vermeyecektir</string>
<string name="settings_su_auth_title">Kullanıcı Kimlik Doğrulama</string>
<string name="settings_su_auth_summary">Süper Kullanıcı isteklerinde kullanıcı kimlik doğrulaması iste</string>
<string name="settings_su_reauth_title">Yükseltmeden Sonra Yeniden Kimlik Doğrula</string>
<string name="settings_su_reauth_summary">Uygulamaları yükselttikten sonra tekrar Süper Kullanıcı izinlerini iste</string>
<string name="settings_su_tapjack_title">Dokunma Saldırısı Koruması</string>
<string name="settings_su_tapjack_summary">Süper Kullanıcı istek penceresi, başka bir pencere veya katman tarafından engellendiğinde girdilere yanıt vermeyecektir</string>
<string name="settings_su_auth_title">Kullanıcı Kimlik Doğrulaması</string>
<string name="settings_su_auth_summary">Süper Kullanıcı istekleri sırasında kullanıcı kimlik doğrulaması iste</string>
<string name="settings_su_auth_insecure">Cihazda yapılandırılmış bir kimlik doğrulama yöntemi yok</string>
<string name="settings_customization">Özelleştir</string>
<string name="setting_add_shortcut_summary">Uygulamayı gizledikten sonra adı ve simgeyi tanımakta zorlanıyorsanız ana ekrana güzel bir kısayol ekle</string>
<string name="settings_doh_title">DNS üzerinden HTTPS</string>
<string name="settings_doh_description">Bazı ülkelerde DNS zehirlemesine karşı geçici çözüm</string>
<string name="settings_random_name_title">Çıkış adını rastgele seç</string>
<string name="settings_random_name_description">Algılamayı önlemek için yamalı resimlerin ve tar dosyalarının çıkış dosya adını rastgele seç</string>
<string name="multiuser_mode">Çok Kullanıcılı Mod</string>
<string name="settings_owner_only">Yalnızca Cihaz Sahibi</string>
<string name="settings_owner_manage">Cihaz Sahibi Yönetiminde</string>
<string name="settings_su_restrict_title">Root Yeteneklerini Kısıtla</string>
<string name="settings_su_restrict_summary">Yeni Süper Kullanıcı uygulamalarını varsayılan olarak kısıtlayacaktır. Uyarı: Bu, çoğu uygulamayı bozacaktır. Ne yaptığınızı bilmiyorsanız etkinleştirmeyin.</string>
<string name="settings_customization">Özelleştirme</string>
<string name="setting_add_shortcut_summary">Uygulamayı gizledikten sonra adı ve simgesi tanınması zor olursa ana ekrana güzel bir kısayol ekleyin</string>
<string name="settings_doh_title">HTTPS üzerinden DNS</string>
<string name="settings_doh_description">Bazı ülkelerdeki DNS zehirlenmesini aşmak için geçici çözüm</string>
<string name="settings_random_name_title">Çıktı Adını Rastgele Yap</string>
<string name="settings_random_name_description">Tespit edilmesini önlemek için yamalanmış imajların ve tar dosyalarının çıktı dosya adını rastgele yapın</string>
<string name="multiuser_mode">Çoklu Kullanıcı Modu</string>
<string name="settings_owner_only">Sadece Cihaz Sahibi</string>
<string name="settings_owner_manage">Cihaz Sahibi Tarafından Yönetilen</string>
<string name="settings_user_independent">Kullanıcıdan Bağımsız</string>
<string name="owner_only_summary">Yalnızca sahip kök (root) erişimine sahiptir</string>
<string name="owner_manage_summary">Yalnızca sahip kök (root) erişimini yönetebilir ve istek uyarılarını alabilir</string>
<string name="user_independent_summary">Her kullanıcının kendi ayrı kök (root) kuralları vardır</string>
<string name="owner_only_summary">Sadece cihaz sahibi root erişimine sahiptir</string>
<string name="owner_manage_summary">Sadece cihaz sahibi root erişimini yönetebilir ve istekleri alabilir</string>
<string name="user_independent_summary">Her kullanıcının kendi ayrı root kuralları vardır</string>
<string name="mount_namespace_mode">Bağlama Ad Alanı Modu</string>
<string name="settings_ns_global">Küresel Ad Alanı</string>
<string name="settings_ns_global">Genel Ad Alanı</string>
<string name="settings_ns_requester">Ad Alanını Devral</string>
<string name="settings_ns_isolate">İzolasyon Ad Alanı</string>
<string name="global_summary">Tüm kök (root) oturumları küresel bağlama ad alanını kullanır</string>
<string name="requester_summary">Kök (root) oturumları isteyicisinin ad alanını devralacak</string>
<string name="isolate_summary">Her kök (root) oturumu kendi izole ad alanına sahip olacak</string>
<string name="settings_ns_isolate">Yalıtılmış Ad Alanı</string>
<string name="global_summary">Tüm root oturumları genel bağlama ad alanını kullanır</string>
<string name="requester_summary">Root oturumları, istekçilerinin ad alanını devralır</string>
<string name="isolate_summary">Her root oturumunun kendi yalıtılmış ad alanı olacaktır</string>
<!--Notifications-->
<string name="update_channel">Magisk Güncellemeleri</string>
<string name="progress_channel">İlerleme Bildirimleri</string>
<string name="updated_channel">Güncelleme Tamamlandı</string>
<string name="update_channel">Magisk güncellemeleri</string>
<string name="progress_channel">İlerleme bildirimleri</string>
<string name="updated_channel">Güncelleme tamamlandı</string>
<string name="download_complete">İndirme tamamlandı</string>
<string name="download_file_error">Dosya indirilirken hata oluştu</string>
<string name="magisk_update_title">Magisk Güncellemesi Mevcut!</string>
<string name="updated_title">Magisk Güncellendi</string>
<string name="magisk_update_title">Magisk güncellemesi mevcut!</string>
<string name="updated_title">Magisk güncellendi</string>
<string name="updated_text">Uygulamayı açmak için dokunun</string>
<!--Toasts, Dialogs-->
<string name="yes">Mevcut</string>
<string name="no">Mevcut Değil</string>
<string name="repo_install_title">%1$s %2$s(%3$d) Kur</string>
<string name="yes">Evet</string>
<string name="no">Hayır</string>
<string name="repo_install_title">%1$s %2$s(%3$d) Yüklensin mi?</string>
<string name="download">İndir</string>
<string name="reboot">Yeniden Başlat</string>
<string name="close">Kapat</string>
<string name="release_notes">Sürüm Notları</string>
<string name="flashing">Yükleniyor...</string>
<string name="done">Tamamlandı!</string>
<string name="flashing">Flaşlanıyor…</string>
<string name="running">Çalışıyor…</string>
<string name="done">Bitti!</string>
<string name="done_action">%1$s eylemi tamamlandı</string>
<string name="failure">Başarısız!</string>
<string name="hide_app_title">Magisk uygulaması gizleniyor…</string>
<string name="open_link_failed_toast">Bağlantıyımak için uygulama bulunamadı</string>
<string name="open_link_failed_toast">Bağlantıyıacak bir uygulama bulunamadı</string>
<string name="complete_uninstall">Tamamen Kaldır</string>
<string name="restore_img">Görüntüleri Geri Yükle</string>
<string name="restore_img_msg">Geri Yükleniyor...</string>
<string name="restore_img">İmajları Geri Yükle</string>
<string name="restore_img_msg">Geri yükleniyor</string>
<string name="restore_done">Geri yükleme tamamlandı!</string>
<string name="restore_fail">Stok yedeği mevcut değil!</string>
<string name="setup_fail">Kurulum başarısız oldu</string>
<string name="env_fix_title">Ek Ayar Gerekiyor</string>
<string name="env_fix_msg">Magisk\'in düzgün çalışabilmesi için cihazınızın ek ayarlar yapması gerekiyor. Devam etmek ve yeniden başlatmak istiyor musunuz?</string>
<string name="env_full_fix_msg">Cihazınızın düzgün çalışabilmesi için Magisk\'in yeniden yüklenmesi gerekiyor. Lütfen Magisk\'i uygulama içinde yeniden yükleyin, kurtarma modu doğru cihaz bilgilerini alamaz.</string>
<string name="setup_msg">Ortam kurulumu yapılıyor...</string>
<string name="env_fix_title">Ek Kurulum Gerektiriyor</string>
<string name="env_fix_msg">Cihazınızın Magisk\'in düzgün çalışması için ek kuruluma ihtiyacı var. Devam edip yeniden başlatmak ister misiniz?</string>
<string name="env_full_fix_msg">Cihazınızın düzgün çalışması için Magisk\'i yeniden flaşlamanız gerekiyor. Lütfen Magisk\'i uygulama içinden yeniden kurun, Kurtarma modu doğru cihaz bilgisini alamaz.</string>
<string name="setup_msg">Çalışma ortamı kuruluyor…</string>
<string name="unsupport_magisk_title">Desteklenmeyen Magisk Sürümü</string>
<string name="unsupport_magisk_msg">Bu uygulama sürümü, %1$s altındaki Magisk sürümlerini desteklemiyor.\n\nUygulama, Magisk yüklü değilmiş gibi davranacaktır, lütfen en kısa sürede Magisk\'i güncelleyin.</string>
<string name="unsupport_magisk_msg">Uygulamanın bu sürümü, %1$s sürümünden daha düşük Magisk sürümlerini desteklemiyor.\n\nUygulama, Magisk yüklü değilmiş gibi davranacaktır. Lütfen Magisk\'i en kısa sürede güncelleyin.</string>
<string name="unsupport_general_title">Anormal Durum</string>
<string name="unsupport_system_app_msg">Bu uygulamanın sistem uygulaması olarak çalıştırılması desteklenmiyor. Lütfen uygulamayı kullanıcı uygulamasına geri döndürün.</string>
<string name="unsupport_other_su_msg">Magisk\'ten gelmeyen bir "su" ikili dosyası tespit edildi. Lütfen herhangi bir rakip kök (root) çözümünü kaldırın ve/veya Magisk\'i yeniden yükleyin.</string>
<string name="unsupport_external_storage_msg">Magisk harici depolamaya yüklendi. Lütfen uygulamayı dahili depolamaya taşıyın.</string>
<string name="unsupport_nonroot_stub_msg">Gizli Magisk uygulaması kök (root) erişimi kaybolduğu için çalışmaya devam edemez. Lütfen orijinal APK\'yı geri yükleyin.</string>
<string name="unsupport_system_app_msg">Bu uygulamanın bir sistem uygulaması olarak çalıştırılması desteklenmiyor. Lütfen uygulamayı bir kullanıcı uygulamasına geri döndürün.</string>
<string name="unsupport_other_su_msg">Magisk\'e ait olmayan bir "su" ikili dosyası tespit edildi. Lütfen rakip root çözümlerini kaldırın ve/veya Magisk\'i yeniden yükleyin.</string>
<string name="unsupport_external_storage_msg">Magisk harici depolamaya kurulmuş. Lütfen uygulamayı dahili depolamaya taşıyın.</string>
<string name="unsupport_nonroot_stub_msg">Gizlenmiş Magisk uygulaması, root kaybolduğu için çalışmaya devam edemiyor. Lütfen orijinal APK\'yi geri yükleyin.</string>
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
<string name="external_rw_permission_denied">Bu işlevselliği etkinleştirmek için depolama izni verin</string>
<string name="post_notifications_denied">Bu işlevselliği etkinleştirmek için bildirim izni verin</string>
<string name="install_unknown_denied">Bu işlevselliği etkinleştirmek için "bilinmeyen uygulamaları yükle" iznini verin</string>
<string name="add_shortcut_title">Ana ekrana kısayol ekle</string>
<string name="add_shortcut_msg">Bu uygulamayı gizledikten sonra adı ve simgesi tanınmayabilir. Ana ekrana güzel bir kısayol eklemek ister misiniz?</string>
<string name="app_not_found">Bu lemi gerçekleştirecek uygulama bulunamadı</string>
<string name="external_rw_permission_denied">Bu işlevi etkinleştirmek için depolama izni verin</string>
<string name="post_notifications_denied">Bu işlevi etkinleştirmek için bildirim izni verin</string>
<string name="install_unknown_denied">Bu işlevi etkinleştirmek için "Bilinmeyen uygulamaları yükle" iznini verin</string>
<string name="add_shortcut_title">Ana Ekrana Kısayol Ekle</string>
<string name="add_shortcut_msg">Bu uygulamayı gizledikten sonra adı ve simgesi tanınması zor olabilir. Ana ekrana güzel bir kısayol eklemek ister misiniz?</string>
<string name="app_not_found">Bu eylemi gerçekleştirecek bir uygulama bulunamadı</string>
<string name="reboot_apply_change">Değişiklikleri uygulamak için yeniden başlatın</string>
<string name="restore_app_confirmation">Bu, gizli uygulamayı orijinal uygulamaya geri yükleyecektir. Gerçekten bunu yapmak istiyor musunuz?</string>
<string name="restore_app_confirmation">Bu, gizlenmiş uygulamayı orijinal uygulamaya geri yükleyecektir. Bunu gerçekten yapmak istiyor musunuz?</string>
</resources>

View File

@@ -54,6 +54,7 @@
<string name="touch_filtered_warning">由于某个应用遮挡了超级用户请求界面,因此 Magisk 无法验证您的回应</string>
<string name="deny">拒绝</string>
<string name="prompt">提示</string>
<string name="restrict">受限</string>
<string name="grant">允许</string>
<string name="su_warning">将授予对该设备的最高权限。\n如果不确定请拒绝</string>
<string name="forever">永久</string>
@@ -173,6 +174,8 @@
<string name="settings_su_auth_title">身份验证</string>
<string name="settings_su_auth_summary">对超级用户请求验证身份</string>
<string name="settings_su_auth_insecure">设备未配置验证方式</string>
<string name="settings_su_restrict_title">限制超级用户权能</string>
<string name="settings_su_restrict_summary">默认限制新的超级用户应用。警告,这会破坏大多数应用,不建议启用。</string>
<string name="settings_customization">个性化</string>
<string name="setting_add_shortcut_summary">在隐藏后难以识别名称和图标的情况下,添加快捷方式到桌面</string>
<string name="settings_doh_title">安全 DNSDoH</string>

View File

@@ -53,6 +53,7 @@
<string name="touch_filtered_warning">Because an app is obscuring a Superuser request, Magisk can\'t verify your response.</string>
<string name="deny">Deny</string>
<string name="prompt">Prompt</string>
<string name="restrict">Restrict</string>
<string name="grant">Grant</string>
<string name="su_warning">Grants full access to your device.\nDeny if you\'re not sure!</string>
<string name="forever">Forever</string>
@@ -170,6 +171,8 @@
<string name="settings_su_auth_title">User authentication</string>
<string name="settings_su_auth_summary">Ask for user authentication during Superuser requests</string>
<string name="settings_su_auth_insecure">No authentication method is configured on the device</string>
<string name="settings_su_restrict_title">Restrict root capabilities</string>
<string name="settings_su_restrict_summary">Will restrict new Superuser apps by default. Warning: this will break most apps. Don\'t enable it unless you know what you\'re doing.</string>
<string name="settings_customization">Customization</string>
<string name="setting_add_shortcut_summary">Add a pretty shortcut to the home screen in case the name and icon are difficult to recognize after hiding the app</string>
<string name="settings_doh_title">DNS over HTTPS</string>

View File

@@ -30,4 +30,4 @@ android.nonFinalResIds=false
# Magisk
magisk.stubVersion=40
magisk.versionCode=30000
magisk.versionCode=30200

View File

@@ -1,17 +1,17 @@
[versions]
kotlin = "2.1.21"
android = "8.11.0"
ksp = "2.1.21-2.0.1"
kotlin = "2.2.0"
android = "8.12.0"
ksp = "2.2.0-2.0.2"
rikka = "1.3.0"
navigation = "2.9.0"
navigation = "2.9.3"
libsu = "6.0.0"
okhttp = "4.12.0"
okhttp = "5.1.0"
retrofit = "3.0.0"
room = "2.7.2"
[libraries]
bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version = "1.81" }
commons-compress = { module = "org.apache.commons:commons-compress", version = "1.27.1" }
commons-compress = { module = "org.apache.commons:commons-compress", version = "1.28.0" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
retrofit-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" }
retrofit-scalars = { module = "com.squareup.retrofit2:converter-scalars", version.ref = "retrofit" }
@@ -41,9 +41,9 @@ transition = { module = "androidx.transition:transition", version = "1.6.0" }
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.5.0" }
material = { module = "com.google.android.material:material", version = "1.12.0" }
jdk-libs = { module = "com.android.tools:desugar_jdk_libs_nio", version = "2.1.5" }
test-runner = { module = "androidx.test:runner", version = "1.6.2" }
test-rules = { module = "androidx.test:rules", version = "1.6.1" }
test-junit = { module = "androidx.test.ext:junit", version = "1.2.1" }
test-runner = { module = "androidx.test:runner", version = "1.7.0" }
test-rules = { module = "androidx.test:rules", version = "1.7.0" }
test-junit = { module = "androidx.test.ext:junit", version = "1.3.0" }
test-uiautomator = { module = "androidx.test.uiautomator:uiautomator", version = "2.3.0" }
# topjohnwu
@@ -62,6 +62,6 @@ android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref
ksp-plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
navigation-safe-args-plugin = { module = "androidx.navigation:navigation-safe-args-gradle-plugin", version.ref = "navigation" }
lsparanoid-plugin = { module = "org.lsposed.lsparanoid:gradle-plugin", version = "0.6.0" }
moshi-plugin = { module = "dev.zacsweers.moshix:dev.zacsweers.moshix.gradle.plugin", version = "0.30.0" }
moshi-plugin = { module = "dev.zacsweers.moshix:dev.zacsweers.moshix.gradle.plugin", version = "0.31.0" }
[plugins]

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -2,7 +2,6 @@
- [Installation Instructions](install.md)
- [Frequently Asked Questions](faq.md)
- [Release Notes](releases/index.md)
- [Magisk Changelog](changes.md)
The following sections are for developers

View File

@@ -1,13 +1,24 @@
# Magisk Changelog
### v30.0
### v30.2 (2025.8.6)
- [Core] Fix an edge case breaking modules when overlayfs is involved
- [Core] Fix module `.replace` functionality in certain situations
- [resetprop] Reduce property modification traces
### v30.1 (2025.7.3)
- [Core] Fix bug in module mounting implementation
- [MagiskSU] Add ability to restrict Linux capabilities even if running as root (uid=0)
### v30.0 (2025.7.1)
- [General] Various minor bug fixes
- [Core] Migrate module implementation to Rust
- [Core] Improve Magisk specific files injection logic
- [MagiskBoot] Migrate compression code to Rust
### v29.0
### v29.0 (2025.5.14)
- [General] Massive internal refactoring and code migration
- [App] Support downloading module zip files with XZ compression
@@ -16,7 +27,7 @@
- [MagiskInit] Redesign sepolicy patching and injection logic
- [MagiskSU] Better TTY/PTY support
### v28.1
### v28.1 (2024.12.6)
- [App] Fix stub APK download link
- [App] Fix support for Android lower than 8.0
@@ -24,7 +35,7 @@
- [MagiskInit] Fix a regression for 2SI devices
- [MagiskPolicy] Fix a regression causing `overlay.d` replaced files to be not accessible
### v28.0
### v28.0 (2024.10.10)
- [General] Support 16k page size
- [General] Add basic support for RISC-V (not built in releases)
@@ -48,7 +59,7 @@
- [MagiskBoot] Properly support vendor boot images
- [MagiskBoot] Disable Samsung PROCA from kernel image
### v27.0
### v27.0 (2024.2.3)
- [Zygisk] Introduce new code injection mechanism
- [Zygisk] Support new signature introduced in U QPR2
@@ -56,7 +67,7 @@
- [MagiskBoot] Support compressing `init` so Magisk is installable on devices with small boot partitions
- [ResetProp] Add new wait for property feature `resetprop -w`
### v26.4
### v26.4 (2023.11.5)
- [MagiskBoot] Don't pad zeros if signed boot image is larger
- [MagiskPolicy] Fix `genfscon` and `filename_trans`
@@ -67,14 +78,14 @@
- [Daemon] Fix certificate parsing of APKs
- [General] Fix logging errors from C++ code being ignored
### v26.3
### v26.3 (2023.9.4)
- [General] Fix device information detection script
- [General] Update BusyBox to 1.36.1
- [General] Update toolchain that produces broken arm32 executables
- [App] Fix root service unable to bind on OnePlus devices
### v26.2
### v26.2 (2023.8.27)
- [MagiskBoot] Support extracting boot image from `payload.bin`
- [MagiskBoot] Support cpio files containing character files
@@ -92,13 +103,13 @@
- [App] Support patching boot image from ROM zips
- [App] Properly preserve `boot.img` when patching Samsung firmware with `init_boot.img`
### v26.1
### v26.1 (2023.4.11)
- [App] Fix crashing when revoking root permissions
- [MagiskInit] Always prefer `ext4` partitions over `f2fs` when selecting the pre-init partition
- [General] Restore module files' context/owner/group from mirror. This is a regression introduced in v26.0
### v26.0
### v26.0 (2023.4.5)
- [General] Bump minimum supported Android version to Android 6.0
- [General] New magic mount backend. It supports loading modules into system with `overlayfs` files injected
@@ -115,7 +126,7 @@
- [MagiskPolicy] Fix minor bug in command line argument parsing
- [MagiskPolicy] Update rules to support Android U
### v25.2
### v25.2 (2022.7.20)
- [MagiskInit] Fix a potential issue when stub cpio is used
- [MagiskInit] Fix reboot to recovery when stub cpio is used
@@ -123,7 +134,7 @@
- [General] Better data encryption detection
- [General] Move the whole logging infrastructure into Rust
### v25.1
### v25.1 (2022.6.19)
- [MagiskBoot] Fix ramdisk backup being incorrectly skipped
- [MagiskBoot] Add new feature to detect unsupported dtb and abort during installation
@@ -132,7 +143,7 @@
- [MagiskInit] Fix config not properly exported in legacy SAR devices
- [General] Enforce the Magisk app to always match or be newer than `magiskd`
### v25.0
### v25.0 (2022.6.7)
- [MagiskInit] Update 2SI implementation, significantly increase device compatibility (e.g. Sony Xperia devices)
- [MagiskInit] Introduce new `sepolicy` injection mechanism
@@ -150,13 +161,13 @@
- [DenyList] Fix DenyList on shared UID apps
- [BusyBox] Add workaround for devices running old kernels
### v24.3
### v24.3 (2022.3.10)
- [General] Stop using `getrandom` syscall
- [Zygisk] Update API to v3, adding new fields to `AppSpecializeArgs`
- [App] Improve app repackaging installation workflow
### v24.2
### v24.2 (2022.3.1)
- [MagiskSU] Fix buffer overflow
- [MagiskSU] Fix owner managed multiuser superuser settings
@@ -174,11 +185,11 @@
- [App] Major app upgrade flow improvements
- [General] Improve commandline error handling and messaging
### v24.1
### v24.1 (2022.1.28)
- [App] Stability improvements
### v24.0
### v24.0 (2022.1.26)
- [General] MagiskHide is removed from Magisk
- [General] Support Android 12
@@ -207,7 +218,7 @@
- [App] Restore the ability to install Magisk on the other slot on some A/B devices
- [App] Allow modules to specify an update URL for in-app update + install
### v23.0
### v23.0 (2021.5.12)
- [App] Update snet extension. This fixes SafetyNet API errors.
- [App] Fix a bug in the stub app that causes APK installation to fail
@@ -221,7 +232,7 @@
- [MagiskHide] Update package and process name validation logic
- [MagiskHide] Some changes that prevents zygote deadlock
### v22.1
### v22.1 (2021.4.9)
- [App] Prevent multiple installation sessions running in parallel
- [App] Prevent OutOfMemory crashes when checking boot signature on PXA boot images
@@ -236,7 +247,7 @@
- [MagiskInit] Fix `sepolicy.rule` mounting strategy
- [resetprop] Always delete existing `ro.` props before updating. This will fix bootloops that could be caused by modifying device fingerprint properties.
### v22.0
### v22.0 (2021.2.23)
- [General] Magisk and Magisk Manager is now merged into the same package!
- [App] The term "Magisk Manager" is no longer used elsewhere. We refer it as the Magisk app.
@@ -248,18 +259,18 @@
- [MagiskInit] Support Galaxy S21 series
- [MagiskSU] Fix incorrect APEX paths that caused `libsqlite.so` fail to load
### v21.4
### v21.4 (2021.1.17)
- [MagiskSU] Fix `su -c` behavior that broke many root apps
- [General] Properly handle read/write over sockets (the `broken pipe` issue)
### v21.3
### v21.3 (2021.1.16)
- [MagiskInit] Avoid mounting `f2fs` userdata as it may result in kernel crashes. This shall fix a lot of bootloops
- [MagiskBoot] Fix a minor header checksum bug for `DHTB` header and ASUS `blob` image formats
- [MagiskHide] Allowing hiding isolated processes if the mount namespace is separated
### v21.2
### v21.2 (2020.12.28)
- [MagiskInit] Detect 2SI after mounting `system_root` on legacy SAR devices
- [General] Make sure `post-fs-data` scripts cannot block more than 35 seconds
@@ -268,7 +279,7 @@
- [General] Directly log to file to prevent `logcat` weirdness
- [MagiskBoot] Fix header dump/load for header v3 images
### v21.1
### v21.1 (2020.11.13)
- [MagiskBoot] Support boot header v3 (Pixel 5 and 4a 5G)
- [MagiskBoot] Distinguish `lz4_lg` and `lz4_legacy` (Pixel 5 and 4a 5G)
@@ -283,7 +294,7 @@
- [MagiskHide] Support hiding apps installed in secondary users (e.g. work profile)
- [MagiskHide] Make zygote detection more robust
### v21.0
### v21.0 (2020.10.3)
- [General] Support Android 11 🎉
- [General] Add Safe Mode detection. Disable all modules when the device is booting into Safe Mode.
@@ -303,7 +314,7 @@
- [MagiskBoot] Pad boot images to original size with zeros
- [MagiskHide] Manipulate additional vendor properties
### v20.4
### v20.4 (2020.3.23)
- [MagiskInit] Fix potential bootloop in A-only 2SI devices
- [MagiskInit] Properly support Tegra partition naming
@@ -321,11 +332,11 @@
- [Scripts] Better addon.d (both v1 and v2) support
- [Scripts] Support Lineage Recovery for Android 10+
### v20.3
### v20.3 (2020.1.10)
- [MagiskBoot] Fix `lz4_legacy` decompression
### v20.2
### v20.2 (2020.1.2)
- [MagiskSU] Properly handle communication between daemon and application (root request prompt)
- [MagiskInit] Fix logging in kmsg
@@ -333,7 +344,7 @@
- [General] Support pre-init sepolicy patch in modules
- [Scripts] Update magisk stock image backup format
### v20.1
### v20.1 (2019.11.2)
- [MagiskSU] Support component name agnostic communication (for stub APK)
- [MagiskBoot] Set proper `header_size` in boot image headers (fix vbmeta error on Samsung devices)
@@ -342,7 +353,7 @@
- [General] Move acct to prevent daemon being killed
- [General] Make sure "--remove-modules" will execute uninstall.sh after removal
### v20.0
### v20.0 (2019.10.11)
- [MagiskBoot] Support inject/modify `mnt_point` value in DTB fstab
- [MagiskBoot] Support patching QCDT
@@ -352,7 +363,7 @@
- [MagiskHide] Fix bug that reject process names with ":"
- [MagicMount] Fix a bug that cause /product mirror not created
### v19.4
### v19.4 (2019.9.19)
- [MagiskInit] [SAR] Boot system-as-root devices with system mounted as /
- [MagiskInit] [2SI] Support 2-stage-init for A/B devices (Pixel 3 Android 10)
@@ -368,7 +379,7 @@
- [General] Add new `--remove-modules` command to remove modules without root in ADB shell
- [General] Support Android 10 new APEX libraries (Project Mainline)
### v19.3
### v19.3 (2019.6.5)
- [MagiskHide] Hugely improve process monitor implementation, hopefully should no longer cause 100% CPU and daemon crashes
- [MagiskInit] Wait for partitions to be ready for early mount, should fix bootloops on a handful of devices
@@ -376,7 +387,7 @@
- [MagiskSU] Properly implement mount namespace isolation
- [MagiskBoot] Proper checksum calculation for header v2
### v19.2
### v19.2 (2019.5.20)
- [General] Fix uninstaller
- [General] Fix bootloops on some devices with tmpfs mounting to /data
@@ -385,7 +396,7 @@
This fix issues with users locking Magisk Manager with app lock, and prevent
video apps get messed up when an app is requesting root in the background.
### v19.1
### v19.1 (2019.5.1)
- [General] Support recovery based Magisk
- [General] Support Android Q Beta 2
@@ -395,7 +406,7 @@
- [MagicMount] Use self created device nodes for mirrors
- [MagicMount] Do not allow adding new files/folders in partition root folder (e.g. /system or /vendor)
### v19.0
### v19.0 (2019.3.28)
- [General] Remove usage of magisk.img
- [General] Add 64 bit magisk binary for native 64 bit support
@@ -413,14 +424,14 @@
- [MagiskSU] Use `ACTION_REBOOT` intent to workaround some OEM broadcast restrictions
- [General] Use `skip_mount` instead of `auto_mount`: from opt-in to opt-out
### v18.1
### v18.1 (2019.2.4)
- [General] Support EMUI 9.0
- [General] Support Kirin 960 devices
- [General] Support down to Android 4.2
- [General] Major code base modernization under-the-hood
### v18.0
### v18.0 (2018.12.8)
- [General] Migrate all code base to C++
- [General] Modify database natively instead of going through Magisk Manager
@@ -442,7 +453,7 @@
- [MagiskBoot] Try to repair broken v1 boot image headers
- [MagiskBoot] Add new CPIO command: "exists"
### v17.3
### v17.3 (2018.10.20)
- [MagiskBoot] Support boot image header v1 (Pixel 3)
- [MagiskSU] No more linked lists for caching `su_info`
@@ -453,13 +464,13 @@
- [Scripts] Switch hexpatch to remove Samsung Defex to a more general pattern
- [Scripts] Update data encryption detection for better custom recovery support
### v17.2
### v17.2 (2018.9.21)
- [ResetProp] Update to AOSP upstream to support serialized system properties
- [MagiskInit] Randomize Magisk service names to prevent detection (e.g. FGO)
- [MagiskSU] New communication scheme to communicate with Magisk Manager
### v17.0/17.1
### v17.0/17.1 (2018.9.1)
- [General] Bring back install to inactive slot for OTAs on A/B devices
- [Script] Remove system based root in addon.d
@@ -471,7 +482,7 @@
- [MagiskHide] Kill all processes with same UID of the target to workaround OOS embryo optimization
- [MagiskInit] Move all sepolicy patches pre-init to prevent Pixel 2 (XL) boot service breakdown
### v16.7
### v16.7 (2018.7.19)
- [Scripts] Fix boot image patching errors on Android P (workaround the strengthened seccomp)
- [MagiskHide] Support hardlink based ns proc mnt (old kernel support)
@@ -479,7 +490,7 @@
- [Daemon] Log fatal errors only on debug builds
- [MagiskInit] Detect early mount partname from fstab in device tree
### v16.6
### v16.6 (2018.7.8)
- [General] Add wrapper script to overcome weird `LD_XXX` flags set in apps
- [General] Prevent bootloop when flashing Magisk after full wipe on FBE devices
@@ -501,7 +512,7 @@
- [ImgTool] Use precise free space calculation methods
- [ImgTool] Use our own set of loop devices hidden along side with sbin tmpfs overlay. This not only eliminates another possible detection method, but also fixes apps that mount OBB files as loop devices (huge thanks to dev of Pzizz for reporting this issue)
### v16.4
### v16.4 (2018.4.29)
- [Daemon] Directly check logcat command instead of detecting logd, should fix logging and MagiskHide on several Samsung devices
- [Daemon] Fix startup Magisk Manager APK installation on Android P
@@ -515,17 +526,17 @@
- [resetprop] Add Protobuf encode/decode to support manipulating persist properties on Android P
- [MagiskHide] Include app sub-services as hiding targets. This might significantly increase the amount of apps that could be properly hidden
### v16.3
### v16.3 (2018.3.28)
- [General] Remove symlinks used for backwards compatibility
- [MagiskBoot] Fix a small size calculation bug
### v16.2
### v16.2 (2018.3.18)
- [General] Force use system binaries in handling ext4 images (fix module installation on Android P)
- [MagiskHide] Change property state to disable if logd is disabled
### v16.1
### v16.1 (2018.3.11)
- [MagiskBoot] Fix MTK boot image packaging
- [MagiskBoot] Add more Nook/Acclaim headers support
@@ -535,13 +546,13 @@
- [resetprop] Support Android P new property context files
- [MagiskPolicy] Add new rules for Android P
### v16.0
### v16.0 (2018.2.22)
- [MagiskInit] Support non `skip_initramfs` devices with slot suffix (Huawei Treble)
- [MagiskPolicy] Add rules for Magisk Manager
- [Compiler] Workaround an NDK compiler bug that causes bootloops
### v15.4
### v15.4 (2018.2.13)
- [MagiskBoot] Support Samsung PXA, DHTB header images
- [MagiskBoot] Support ASUS blob images
@@ -553,13 +564,13 @@
- [Daemon] Obfuscate binary names to prevent naive detections
- [Daemon] Check logd before force trying to start logcat in a loop
### v15.3
### v15.3 (2018.1.12)
- [Daemon] Fix the bug that only one script would be executed in post-fs-data.d/service.d
- [Daemon] Add `MS_SILENT` flag when mounting, should fix some devices that cannot mount magisk.img
- [MagiskBoot] Fix potential segmentation fault when patching ramdisk, should fix some installation failures
### v15.2
### v15.2 (2018.1.1)
- [MagiskBoot] Fix dtb verity patches, should fix dm-verity bootloops on newer devices placing fstabs in dtb
- [MagiskPolicy] Add new rules for proper Samsung support, should fix MagiskHide
@@ -567,17 +578,17 @@
- [Daemon] Use specific logcat buffers, some devices does not support all log buffers
- [scripts] Update scripts to double check whether boot slot is available, some devices set a boot slot without A/B partitions
### v15.1
### v15.1 (2017.12.29)
- [MagiskBoot] Fix faulty code in ramdisk patches which causes bootloops in some config and fstab format combos
### v15.0
### v15.0 (2017.12.26)
- [Daemon] Fix the bug that Magisk cannot properly detect /data encryption state
- [Daemon] Add merging `/cache/magisk.img` and `/data/adb/magisk_merge.img` support
- [Daemon] Update to upstream libsepol to support cutting edge split policy custom ROM cil compilations
### v14.6 (1468)
### v14.6 (2017.12.22)
- [General] Move all files into a safe location: /data/adb
- [Daemon] New invincible implementation: use `magiskinit_daemon` to monitor sockets
@@ -590,12 +601,12 @@
- [MagiskBoot] Massive refactor, rewrite all cpio operations and CLI
- [MagiskInit][magiskboot] Support ramdisk high compression mode
### v14.5 (1456)
### v14.5 (1456) (2017.11.23)
- [Magiskinit] Fix bootloop issues on several devices
- [misc] Build binaries with NDK r10e, should get rid of the nasty linker warning when executing magisk
### v14.5 (1455)
### v14.5 (1455) (2017.11.23)
- [Daemon] Moved internal path to /sbin/.core, new image mountpoint is /sbin/.core/img
- [MagiskSU] Support switching package name, used when Magisk Manager is hidden
@@ -610,7 +621,7 @@
- [script] Add dtbo.img backup and restore support
- [misc] Many small adjustments to properly support old platforms like Android 5.0
### v14.3 (1437)
### v14.3 (2017.10.15)
- [MagiskBoot] Fix Pixel C installation
- [MagiskBoot] Handle special `lz4_legacy` format properly, should fix all LG devices
@@ -620,11 +631,11 @@
- [Daemon] Add brute-force image resizing mode, should prevent the notorious Samsung crappy resize2fs from affecting the result
- [resetprop] Add new "-p" flag, used to toggle whether alter/access the actual persist storage for persist props
### v14.2
### v14.2 (2017.9.28)
- [MagicMount] Clone attributes to tmpfs mountpoint, should fix massive module breakage
### v14.1
### v14.1 (2017.9.28)
- [MagiskInit] Introduce a new init binary to support `skip_initramfs` devices (Pixel family)
- [script] Fix typo in update-binary for x86 devices
@@ -639,7 +650,7 @@
- [resetprop] Fix a bug which delete props won't remove persist props not in memory
- [MagicMount] Remove usage of dummy folder, directly mount tmpfs and construct file structure skeleton in place
### v14.0
### v14.0 (2017.9.6)
- [script] Simplify installation scripts
- [script] Fix a bug causing backing up and restoring stock boot images failure
@@ -658,20 +669,20 @@
- [Daemon] Adjustments to prevent stock Samsung kernel restrictions on exec system calls for binaries started from /data
- [Daemon] Workaround on Samsung device with weird fork behaviors
### v13.3
### v13.3 (2017.7.18)
- [MagiskHide] Update to bypass Google CTS (2017.7.17)
- [resetprop] Properly support removing persist props
- [uninstaller] Remove Magisk Manager and persist props
### v13.2
### v13.2 (2017.7.14)
- [magiskpolicy] Fix magiskpolicy segfault on old Android versions, should fix tons of older devices that couldn't use v13.1
- [MagiskHide] Set proper selinux context while re-linking /sbin to hide Magisk, should potentially fix many issues
- [MagiskBoot] Change lzma compression encoder flag from `LZMA_CHECK_CRC64` to `LZMA_CHECK_CRC32`, kernel only supports latter
- [General] Core-only mode now properly mounts systemless hosts and magiskhide
### v13.1
### v13.1 (2017.7.11)
- [General] Merge MagiskSU, magiskhide, resetprop, magiskpolicy into one binary
- [General] Add Android O support (tested on DP3)
@@ -702,7 +713,7 @@
- [MagiskHide] Remove background magiskhide daemon, spawn short life process for unmounting purpose
- [Magic Mount] Ditched shell script based mounting, use proper C program to parse and mount files. Speed is SIGNIFICANTLY improved
### v12.0
### v12.0 (2017.3.31)
- [General] Move most binaries into magisk.img (Samsung cannot run su daemon in /data)
- [General] Move sepolicy live patch to `late_start` service
@@ -719,7 +730,7 @@
- [MagiskBoot] Add lz4 legacy format support (most linux kernel using lz4 for compression is using this)
- [MagiskBoot] Fix MTK kernels with MTK headers
### v11.5/11.6
### v11.5/11.6 (2017.3.21)
- [Magic Mount] Fix mounting issues with devices that have separate /vendor partitions
- [MagiskBoot] Whole new boot image patching tool, please check release note for more info
@@ -731,12 +742,12 @@
- [MagiskSU] Fix read-only partition mounting issues
- [MagiskSU] Disable -cn option, the option will do nothing, preserved for compatibility
### v11.1
### v11.1 (2017.2.6)
- [sepolicy-inject] Add missing messages
- [magiskhide] Start MagiskHide with scripts
### v11.0
### v11.0 (2017.2.6)
- [Magic Mount] Support replacing symlinks.
Symlinks cannot be a target of a bind mounted, so they are treated the same as new files
@@ -764,13 +775,13 @@
- [Addition] Add post-fs-data.d and service.d
- [Addition] Add option to disable Magisk (MagiskSU will still be started)
### v10.2
### v10.2 (2017.1.2)
- [Magic Mount] Remove apps/priv-app from whitelist, should fix all crashes
- [phh] Fix binary out-of-date issue
- [scripts] Fix root disappear issue when upgrading within Magisk Manager
### v10
### v10 (2017.1.2)
- [Magic Mount] Use a new way to mount system (vendor) mirrors
- [Magic Mount] Use universal way to deal with /vendor, handle both separate partition or not
@@ -784,7 +795,7 @@
- [scripts] Improve SuperSU integration, now uses sukernel to patch ramdisk, support SuperSU built in ramdisk restore
- [template] Add PROPFILE option to load system.prop
### v9
### v9 (2016.11.14)
- **[API Change] Remove the interface for post-fs modules**
- [resetprop] New tool "resetprop" is added to Magisk to replace most post-fs modules' functionality
@@ -796,13 +807,13 @@
- [Boot Image] Add support for Motorola boot image dtb, it shall now unpack correctly
- [Uninstaller] Add removal of SuperSU custom patch script
### v8
### v8 (2016.10.19)
- Add Magisk Hide to bypass SafetyNet
- Improve SuperSU integration: no longer changes the SuperSU PATH
- Support rc script entry points not located in init.rc
### v7
### v7 (2016.10.04)
- Fully open source
- Remove supolicy dependency, use my own sepolicy-injection
@@ -814,17 +825,17 @@
- New paths to toggle busybox, and support all root solutions
- Remove root management API; both SuperSU and phh has their own superior solutions
### v6
### [v6 (2016.8.21)](https://xdaforums.com/t/magisk-general-support-discussion.3432382/post-68298121)
- Fixed the algorithm for adding new files and dummy system
- Updated the module template with a default permission, since people tend to forget them :)
### v5
### [v5 (2016.8.20)](https://xdaforums.com/t/magisk-general-support-discussion.3432382/post-68274534)
- Hotfix for older Android versions (detect policy before patching)
- Update uninstaller to NOT uninstall Magisk Manager, since it cause problems
### v4
### [v4 (2016.8.19)](https://xdaforums.com/t/magisk-general-support-discussion.3432382/post-68269300)
- Important: Uninstall v1 - v3 Magisk before upgrading with the uninstaller in the OP!!
- Massive Rewrite Magisk Interface API! All previous mods are NOT compatible! Please download the latest version of the mods you use (root/xposed)
@@ -833,7 +844,7 @@
- Use minimal sepolicy patch in boot image for smaller ramdisk size. Live patch policies after bootup
- Include updated open source sepolicy injection tool (source code available), support nearly all SuperSU supolicy tool's functionality
### v3
### [v3 (2016.8.11)](https://xdaforums.com/t/magisk-general-support-discussion.3432382/post-68146978)
- Fix bootimg-extract for Exynos Samsung devices (thanks to @phhusson), should fix all Samsung device issues
- Add supolicy back to patch sepolicy (stock Samsung do not accept permissive domain)
@@ -843,7 +854,7 @@
- Use the highest possible compression rate for ramdisk, hope to fix some devices with no boot partition space
- Detect boot partition space insufficient, will abort installer instead of breaking your device
### v2
### [v2 (2016.8.9)](https://xdaforums.com/t/magisk-general-support-discussion.3432382/post-68108058)
- Fix verity patch. It should now work on all devices (might fix some of the unable-to-boot issues)
- All scripts will now run in selinux permissive mode for maximum compatibility (this will **NOT** turn your device to permissive)
@@ -853,6 +864,6 @@
- Remove sepolicy patches that uses SuperSU's supolicy tool; it is now using a minimal set of modifications
- Removed Magisk Manager in Magisk patch, it is now included in Magisk phh's superuser only
### v1
### [v1 (2016.8.3)](https://xdaforums.com/t/magisk-general-support-discussion.3432382/post-68034103)
- Initial release

View File

@@ -16,9 +16,17 @@ The following details should ensure that modules are properly disabled:
Magisk no longer handles root hiding. There are plenty of Magisk/Zygisk modules available that specifically provide these functionalities, please search around 😉
### Q: After I hidden the Magisk app, the app icon is broken.
### Q: Magisk App shows Magisk Installed = N/A after an update but magisk su is still working.
When hiding the Magisk app, it will install a "stub" APK that has nothing in it. The only functionality this stub app has is downloading the full Magisk app APK into its internal storage and dynamically loading it. Due to the fact that the APK is literally _empty_, it does not contain the image resource for the app icon.
If upgrading with App hidden (ie. you took the 'Hide the Magisk app' option), the stub app (for hiding Magisk) may remain while a full Magisk app is also installed. This creates a conflict and the full app fails to see or access root... Uninstalling and reinstalling the full app can fix this, but not if a hidden app (stub) still exists.
The solution is to check for a hidden stub app and remove it. It may not show up normally in your launcher homescreen any longer, but should be visible from general settings, Apps. The hidden app will be named 'Settings' (default) or whatever you named it during the hiding process. Note that it is possible to have multiple obfuscated apps present. Uninstall any iterations of the hidden app you find and try opening the full app again. If necessary, uninstall it and reinstall the full app matching the binaries installed. Typing magisk -c in a terminal emulator app will show the version and version code for Magisk binaries installed (despite Installed = N/A showing).
Additionally, if a 'second space', eg. Workspace, Parallel Space etc, or another sandboxed environment, eg. a Multiple User additional profile, Island app or similar, is set up, check that no iterations of Magisk (either hidden or full apps) are running within these environments.
### Q: After I take the 'Hide the Magisk app' option the app icon is broken.
When hiding the Magisk app, it will install a "stub" APK that has nothing in it. The only functionality this stub app has is to download the full Magisk app APK data into its internal storage and dynamically load it. Due to the fact that the stub APK is literally empty, it does not contain the image resource for the app icon.
When you open the hidden Magisk app, it will offer you the option to create a shortcut in the homescreen (which has both the correct app name and icon) for your convenience. You can also manually ask the app to create the icon in app settings.

View File

@@ -1,30 +0,0 @@
## 2018.12.7 Magisk v18.0
Here comes a stable release, this time with quite a few major updates!
### MagiskHide Improvements
Starting from v18, the process monitor matches component names instead of process names. Android allow app services to name their process arbitrarily, and many apps starting to use dedicated services to detect root; it used to require adding all of these service process names to the list to hide Magisk effectively. Component names have the format: `<package name>/<java class name>`, which means we can always know which application spawned a given process.
**TL;DR, ALL processes spawned from the applications on the hide list will be targeted.**
Recently I discovered a *very widespread Linux kernel bug* affecting tons of Android devices (full write-up: [Medium Article](https://medium.com/@topjohnwu/from-anime-game-to-android-system-security-vulnerability-9b955a182f20)). This bug exposes the supposedly protected `procfs`, which is abused in some apps to detect Magisk with information leaked from other processes. Magisk will patch this bug on all Android 7.0+ devices. Yes, a fully effective MagiskHide requires the enhanced Android Sandbox in modern Android versions.
### Path Changes
The name of the folder `/sbin/.core` is confusing and will no longer be used; it is replaced with `/sbin/.magisk`. Another major change is the location to store general boot scripts. As these boot scripts should still run even if `magisk.img` is not mounted, they are moved out of `magisk.img`, from `<img>/.core/<stage>.d` to `/data/adb/<stage>.d` (stage is either `post-fs-data` or `service`). Say goodbye to stupid paths like `/sbin/.core/img/.core/post-fs-data.d`!
Quick recap:
- New `magisk.img` mountpoint: `/sbin/.magisk/img`
- New internal busybox PATH: `/sbin/.magisk/busybox`
- The folder `<img>/.core` is no longer used in any places. `magisk.img` is solely used for storing modules, no other functionality depends on it.
- **Symlinks are created so all old paths will still work. None of the existing apps/scripts depending on these internal paths should break, but please migrate to the new paths ASAP.**
### Dropping Legacy Support
**The NEXT Magisk Manager upgrade (not this one) will only support v18+, please upgrade ASAP.** Magisk Manager is always designed to be fully functional across a wide range of Magisk versions. However, to enforce full obfuscation, I will have to drop legacy support eventually.
This is also a good opportunity to push the whole community forward, all module developers should forget about backward compatibility (e.g. stop supporting the old Magisk paths, please don't torture yourself...). I expect very few structural changes in the near future, so again, please upgrade ASAP :)
### Modern C++ Code Base
Although this has nothing to do with the end user, tons of effort was done to migrate Magisk to a more modern C++ code base instead of the previous good plain old C. This makes the code easier to maintain and allows me to utilized many C++ language features.
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,17 +0,0 @@
# 2019.2.4 Magisk v18.1
What is a better way to celebrate Chinese New Year than a new Magisk update!
### EMUI 9 Support
Welcome on board "again", Huawei! Even though Huawei had officially blocked bootloader unlocks, people still love to buy them (duh), and there are paid services that unlock Huawei bootloaders. So hey, get Magisk installed on that bad boy! One caveat is that since Huawei have changed the partitions, special workarounds has to be done. Details and instructions are in the newly created [instruction page](https://topjohnwu.github.io/Magisk/install.html)
### Support Down to Android 4.2
Because why not, it was quite a lot of fun LOL. All devices running KitKat and higher will have all features enabled. MagiskHide and resetprop aren't possible on Jellybean, and Magic Mount (modules) is temporarily disabled; basically it only works as a root solution for now. Android 4.1 isn't 100% usable yet, so installation is also temporarily blocked. Eventually, all Jellybean devices will have full Magic Mount and MagiskSU support.
### Major Magisk Manager Update
Aside from the obvious major UI overhaul, tons of little user experience and performance improvements are also added. The app is finally less crappy now :)
### Final Words
I'm aware that there are apps updated to detect Magisk, however no MagiskHide improvements efforts are done in this release; v18.1 is aimed to be as stable as possible. Stay tuned for future public betas, or if you are more adventurous, jump on the Canary Channel bandwagon for more aggressive hiding techniques :)
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,29 +0,0 @@
# 2019.3.28 Magisk v19.0
I would say this is one of my most ambitious release of all time! Due to the extremely massive changes, this release will be a public beta. Calling it v18.2 doesn't do it justice, so v19.0 we go.
## Magisk Module installer
**Magisk module developers: pay extra attention!** A completely new [Magisk Module Installer](https://github.com/topjohnwu/magisk-module-installer) replaces the old Magisk module template. This new format decouples **ALL** installation logic from modules, and encourages developers to use the provided API for installation. This new format is **ENFORCED**, meaning all existing modules should upgrade ASAP, and new modules are **REQUIRED** to follow the rules.
Carefully read through the [updated docs](https://topjohnwu.github.io/Magisk/guides.html)!
**Warning: All existing modules that does not use the new module format will be automatically removed on May 1st, 2019. Module devs: upgrade your existing modules ASAP!**
## Imageless Magisk
Since the existence of Magisk, all modules are stored within an EXT4 image which will be loop mounted at boot. This approach has a few problems: resizing the image is a huge headache (no live resizing, `resize2fs` on some devices refuse to work properly), and also MANY devices using F2FS ships a broken driver with the kernel, causing EXT4 loop devices unable to be mounted at all. All these problems come to an end now: modules are now directly stored in `/data`! Backwards compatibility is provided, for modules that uses the official module template, installation should work just fine.
**Warning: Although module migration was tested, there are still chances that your modules will get lost in the process. Be prepared to reinstall your existing modules in that case.**
## Native 64 Bit is Back
At one point in history, Magisk uses native 64 binaries. However due to binary size considerations, all binaries was switched to 32 bit. Starting from v19, all static binaries are still 32 bit only, but the most important part: the main `magisk` binary now runs in native 64 bit on supported devices.
## Zygote Ptrace Based MagiskHide
MagiskHide used to use `logcat` to monitor activity manager events for new process creation. That method is extremely unreliable: even with constant improvements since introduction, it is still not working 100% of the time. Here comes a fundamentally new approach: ptrace the zygote process and step through all fork events. In layman's term, this new method is able to target a process before it even starts to run! The code for it is extremely tricky, but it was tested for quite a while in the canary channel, so I'm confident enough to release this to the public :)
## Android Q
Full support for Android Q Beta 1 is also introduced in this release. However, you cannot use it on the Pixel 3 (XL) due to the fact that Google decided to use logical partitions on the 3rd gen Pixels starting with Q. A solution is still WIP, please stay tuned!
## Final Words
What you can expect in upcoming releases: Samsung S10 support, and full logical partition support. Also, I *AM* aware of Google Pay issues, but these are not my main focus now since there are still tons of other issues for me to focus on. Several discussion threads on XDA provide seemingly working solutions, please do some research on your own.
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,12 +0,0 @@
# 2019.5.1 Magisk v19.1
Finally, a lovely stable release!
For those that were using v18.1, here are some quick highlights of v19.0
- Imageless Magisk: Although module migration was tested, there are still chances that your modules will get lost in the process. Be prepared to reinstall your existing modules in that case.
- Native 64-bit support
- Zygote Ptrace Based MagiskHide
Other than adding support for Samsung system-as-root devices, this release is mostly bug fixes from v19.0. Enjoy :)
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1 +0,0 @@
# TODO

View File

@@ -1 +0,0 @@
# TODO

View File

@@ -1,23 +0,0 @@
# 2019.9.19 Magisk v19.4
This version is heavily tested and tons of bugs were squashed before release. However due to the massive changes, it is decided to release a public beta for people/root app developers to adjust/update before things hit public stable.
### New System-as-root Implementation
Magisk has supported system-as-root devices for a long time since the first Pixel came out. The goal is always to revert things back to the good old initramfs based root dir. However, this not only creates tons of issues on many devices, not easily hide-able with MagiskHide, but most importantly not even possible on Android 10. Starting with v19.4, Magisk will follow how Google has designed system-as-root: mounting system actually to `/` (root).
This implies several **MASSIVE** consequences for system-as-root devices:
- `/system` is no longer a valid mount point. For existing root apps that remounts `/system` to `rw`, you will have to remount `/` instead of `/system`
- The root directory (`/`) is no longer `rootfs`, but actually system. Remounting `/` to `rw` and modify files means you are writing to the actual system partition, NOT volatile storage as it used to be in `rootfs`. This is not recommended as user is not necessary aware that you are tampering an actual partition, sometimes dangerous if dm-verity/AVB-verity is enforced, or sometimes outright impossible since many devices now ship with read-only system partitions (e.g. EROFS, EXT4 dedup)
- Several custom kernel rely on Magisk's root directory overlay system (`overlay`) for modifying `/`. This is no longer compatible with the new implementation. A new overlay system (`overlay.d`) will replace the existing one as an alternative (details in [documentations](https://topjohnwu.github.io/Magisk/guides.html#root-directory-overlay-system)). To provide backwards compatibility, Magisk will switch to "Compat Mode" when `/overlay` is detected, which simply reverts to the old system-as-root setup. **Compat Mode will not work on Android 10 and will cause bootloop**. Although things will still work as it used to, **please upgrade to `overlay.d` ASAP**.
### Android 10 Support
Other than A-only devices running Android 10, Android 10 is fully supported with MagiskHide fully functioning. Android 10's biggest challenge is the new "2-Stage-Init" system-as-root implementation, which is the sole reason why A-only is not support yet. Stay tuned for further updates as that is the next thing on the list.
(For those interested in "2-Stage-Init" and other details of system-as-root, check [this Twitter thread I tweeted](https://twitter.com/topjohnwu/status/1174392824625676288))
### Product Partition Support
Magisk Module developers can now finally properly modify files in `/product`! This partition is now an essential part in Android 10, and many files are moved from system to product. Please check [documentations](https://topjohnwu.github.io/Magisk/details.html#magic-mount) for more details.
### A-Only System-as-root
A huge number of new devices have A-only system-as-root setups (Android 9.0). These unfortunate devices will have to install Magisk into the recovery partition. Please check the fully updated [Installation Guide](https://topjohnwu.github.io/Magisk/install.html) for more details.
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,23 +0,0 @@
# 2019.10.11 Magisk v20.0
The following release notes are mostly the same as v19.4. Compared to v19.4 beta, the most notable change is adding tons of support for more devices on Android 10, along with several bug fixes.
### New System-as-root Implementation
Magisk has supported system-as-root devices for a long time since the first Pixel came out. The goal is always to revert things back to the good old initramfs based root dir. However, this not only creates tons of issues on many devices, not easily hide-able with MagiskHide, but most importantly not even possible on Android 10. Magisk will now start to follow how Google has designed system-as-root: mounting system actually to `/` (root).
This implies several **MASSIVE** consequences for system-as-root devices:
- `/system` is no longer a valid mount point. For existing root apps that remounts `/system` to `rw`, you will have to remount `/` instead of `/system`
- The root directory (`/`) is no longer `rootfs`, but actually system. Remounting `/` to `rw` and modify files means you are writing to the actual system partition, NOT volatile storage as it used to be in `rootfs`. This is not recommended as user is not necessary aware that you are tampering an actual partition, sometimes dangerous if dm-verity/AVB-verity is enforced, or sometimes outright impossible since many devices now ship with read-only system partitions (e.g. EROFS, EXT4 dedup)
- Several custom kernel rely on Magisk's root directory overlay system (`overlay`) for modifying `/`. This is no longer compatible with the new implementation. A new overlay system (`overlay.d`) will replace the existing one as an alternative (details in [documentations](https://topjohnwu.github.io/Magisk/guides.html#root-directory-overlay-system)). To provide backwards compatibility, Magisk will switch to "Compat Mode" when `/overlay` is detected, which simply reverts to the old system-as-root setup. **Compat Mode will not work on Android 10 and will cause bootloop**. Although things will still work as it used to, **please upgrade to `overlay.d` ASAP**.
### Android 10 Support
Android 10 is now fully supported with MagiskHide working as expected. Android 10's biggest challenge is the new "2-Stage-Init" system-as-root implementation, which requires modding early mount fstab in a specific way, and in many devices' cases involves patching DTBs in the boot image.
(For those interested in "2-Stage-Init" and other details of system-as-root, check [this Twitter thread I tweeted](https://twitter.com/topjohnwu/status/1174392824625676288))
### Product Partition Support
Magisk Module developers can now finally properly modify files in `/product`! This partition is now an essential part in Android 10, and many files are moved from system to product. Please check [documentations](https://topjohnwu.github.io/Magisk/details.html#magic-mount) for more details.
### A-Only System-as-root
A huge number of new devices have A-only system-as-root setups (Android 9.0). These unfortunate devices will have to install Magisk into the recovery partition. Please check the fully updated [Installation Guide](https://topjohnwu.github.io/Magisk/install.html) for more details.
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,16 +0,0 @@
# 2019.11.2 Magisk v20.1
Lots of bug fixes from v20.0, and some cool new features!
### Updated Magisk Manager Hiding
Starting with Magisk v20.1 paired with Magisk Manager v7.4.0, a new hiding mode is introduced for Android 9.0+. On supported devices, Magisk Manager will download and customize a heavily obfuscated stub APK and use it as a replacement. The stub app will then download the full app into its private internal data, then dynamically load and run the actual full Magisk Manager.
Note, not all Android 9.0+ devices will be able to use this feature. To use an obfuscated stub as Magisk Manager, the Magisk daemon will have to rely on a special way to communicate with the app, and some OEMs (most likely Chinese manufacturers) block certain broadcasts, breaking the communication channel.
Magisk Manager will verify compatibility before it uses stubs to hide itself on Android 9.0+. **The verification relies on Magisk v20.1+, which means you have to fully upgrade and reboot in order to opt in this feature.** If you are already running a hidden Magisk Manager, **restore and upgrade Magisk Manager, upgrade Magisk and reboot, then re-hide the app**.
For those incompatible with the hiding-with-stub feature, there are also a few updates that everyone, regardless whether using stubs or not, can enjoy:
- You can now customize the app name of the repackaged Magisk Manager
- Magisk Manager will generate new keys to sign the repackaged APK to prevent signature detection
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,15 +0,0 @@
# 2020.1.2 Magisk v20.2
Happy New Year! Let's start 2020 with a new Magisk release :)
### Pre-Init sepolicy Patches for Modules
Magisk v20.2 add support for modules to include its own custom sepolicy patches. Developers used to use boot scripts along with the `magiskpolicy` tool to do live sepolicy patches; however, this method leads to numerous issues as Android is no longer designed to allow live sepolicy patches, and on some devices (e.g. Huawei) this method is outright inapplicable.
To address this issue, Magisk allow module devs to create a new file called `sepolicy.rule` in their modules. The module installer script and Magisk daemon will make sure this file is stored in somewhere accessible pre-init to allow `magiskinit` to do its job every time your device boots up.
### New Module Installer Format
The old template is actually pretty convoluted: developers are expected to implement specific callback functions in their `install.sh`, and the zip file structure does not directly represent how modules are actually stored on your device. The new module installer format makes creating new modules very easy, but still give experienced developers tons of freedom to do anything they want in the installation process.
For details regarding `sepolicy.rule` and the new module installer format, please read the updated [Developer Guides](https://topjohnwu.github.io/Magisk/guides.html). Note that the old "Module Installer Template" is obsolete; creating a Magisk module no longer requires a "template" as it is now a straightforward process.
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,9 +0,0 @@
# 2020.1.10 Magisk v20.3
### Magisk
- Fix `magiskboot` crashing when dealing with `lz4_legacy` format
### Magisk Manager
- Fix MagiskHide app component toggles
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,28 +0,0 @@
## 2020.3.23 Magisk v20.4
### Miscellaneous
This release is mainly focused on stability and bug squashing. Please be aware that MagiskHide is no longer enabled by default. Since Google has enabled [hardware-based key attestation](https://twitter.com/topjohnwu/status/1237656703929180160?s=20) in SafetyNet ([FAQ](https://twitter.com/topjohnwu/status/1237830555523149824?s=20)), there is no effective way to pass full CTS SafetyNet anymore (although Google seems to have temporarily [reverted the change](https://twitter.com/topjohnwu/status/1238514375150850048?s=20)).
I decided that the fully redesigned Magisk Manager isn't fully ready for prime time yet, so this time around no Magisk Manager update is released. The WIP manager will continue to be improved and is available for testing on the canary channel.
### BusyBox Standalone Mode
Starting with Magisk v20.4, all Magisk related scripts, including boot scripts and module installation scripts, will run on BusyBox's shell (ash) in **standalone mode**. In BusyBox ash standalone mode, **every single command** will be **forced** to use the one that is in Magisk's BusyBox (if available). For instance, no matter how you change the environment variable `PATH`, the `rm` command will always use the one in BusyBox, not the one in system, external BusyBox, vendor, or included in custom recovery.
The reason behind this change is that all scripts will be guaranteed to have 100% consistent results no matter how the environment is setup. The internal BusyBox is significantly beefed up with patches from @osm0sis, and also with SELinux features enabled. It shall offer a very complete, reliable, and consistent scripting environment. If in any case you **require** to use a command outside of BusyBox, please call it with the full path (e.g. `/system/bin/nslookup`)
### Magisk Changelog
- [MagiskInit] Fix potential bootloop in A-only 2SI devices
- [MagiskInit] Properly support Tegra partition naming
- [General] Load libsqlite.so dynamically, which removes the need to use wrapper scripts on Android 10+
- [General] Detect API level with a fallback method on some devices
- [General] Workaround possible bug in x86 kernel readlinkat system call
- [BusyBox] Enable SELinux features. Add chcon/runcon etc., and '-Z' option to many applets
- [BusyBox] Introduce standalone mode. More details in release notes
- [MagiskHide] Disable MagiskHide by default
- [MagiskHide] Add more potential detectable system properties
- [MagiskHide] Add workaround for Xiaomi devices bootloop when MagiskHide is enabled on cross region ROMs
- [MagiskBoot] Support patching special Motorolla DTB format
- [MagiskPolicy] Support 'genfscon' sepolicy rules
- [Scripts] Support NAND based boot images (character nodes in /dev/block)
- [Scripts] Better addon.d (both v1 and v2) support
- [Scripts] Support Lineage Recovery for Android 10+

View File

@@ -1,19 +0,0 @@
## 2020.10.3 Magisk v21.0
Long time no see! v21.0 is the largest release in Magisk's history. It comes with full Android 11 support (tons of stuff had to be rewritten from scratch!), and a completely redesigned Magisk Manager. These are the reasons why this particular public release took me over half a year to wrap up.
To the end user, not much has changed other than the fact that Magisk Manager has completely changed its appearance. However developers should pay attention to some changes due to adjustments for Android 11. Full changelogs are too massive to fit, so here I'll point out the main changes and links to updated documentations.
### Highlights
- Android 11 support 🎉
- Completely redesigned Magisk Manager
- Safe Mode detection: if you installed a module that bootloops your device, reboot into Safe Mode and all modules will be disabled. More instructions on how to deal with broken modules is linked [here](https://topjohnwu.github.io/Magisk/faq.html).
The following are for advanced users/developer:
- On Android 8.0+, Magisk now uses a new SELinux setup that keeps Android sandbox less compromised. This provides better security to rooted users, and also separates Magisk rules from original rules. Details [here](https://topjohnwu.github.io/Magisk/details.html#selinux-policies).
- On Android 11, `/sbin` may no longer exist. For developers, this means the Magisk's internal `tmpfs` directory is no longer always `/sbin`, and instead randomly created every boot. To get the `tmpfs` path, use the command `magisk --path` (more details [here](https://topjohnwu.github.io/Magisk/details.html)). For custom kernel developers that uses `overlay.d`, updated docs are [here](https://topjohnwu.github.io/Magisk/guides.html#root-directory-overlay-system).
- `magiskpolicy` gained more features and some minor syntax changes, details [here](https://topjohnwu.github.io/Magisk/tools.html#magiskpolicy).
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,5 +0,0 @@
## 2020.11.13 Magisk v21.1
v21.1 is a maintenance update from v21.0, mostly addressing bugs, refining some details, and adding new boot image format support (for Pixel 5 and 4a 5G). Checkout the full [v21.0 release notes](https://topjohnwu.github.io/Magisk/releases/21000.html) if coming from older releases.
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,14 +0,0 @@
## 2020.12.28 Magisk v21.2
v21.2 is a maintenance update, mostly addressing bugs, and expanding device compatibility. Checkout the full [v21.0 release notes](https://topjohnwu.github.io/Magisk/releases/21000.html) if coming from older releases.
### v21.2
- [MagiskInit] Detect 2SI after mounting `system_root` on legacy SAR devices
- [General] Make sure `post-fs-data` scripts cannot block more than 35 seconds
- [General] Fix the `magisk --install-module` command
- [General] Trim Windows newline when reading files
- [General] Directly log to file to prevent `logcat` weirdness
- [MagiskBoot] Fix header dump/load for header v3 images
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,18 +0,0 @@
## 2021.1.17 Magisk v21.4
**Update**: v21.4 adds more regression hot fixes.
Happy 2021! v21.3 adds a workaround for devices with buggy F2FS Linux kernel drivers. This F2FS bug may cause bootloops on many devices. Checkout the full [v21.0 release notes](https://topjohnwu.github.io/Magisk/releases/21000.html) if coming from older releases.
### v21.4
- [MagiskSU] Fix `su -c` behavior that broke many root apps
- [General] Properly handle read/write over sockets (the `broken pipe` issue)
### v21.3
- [MagiskInit] Avoid mounting `f2fs` userdata as it may result in kernel crashes. This shall fix a lot of bootloops
- [MagiskBoot] Fix a minor header checksum bug for `DHTB` header and ASUS `blob` image formats
- [MagiskHide] Allowing hiding isolated processes if the mount namespace is separated
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,24 +0,0 @@
## 2021.2.23 Magisk v22.0
### RESTORE THE EXISTING MAGISK MANAGER BACK TO NORMAL BEFORE UPGRADING IF HIDDEN!
Another major Magisk release! This time our focus is not the core Magisk implementation, but rather on improving the whole Magisk user experience.
### Magisk Manager is dead.<br>Long live the Magisk app!
Ever since the first Magisk release, Magisk (the core components) and Magisk Manager (the companion app) are released separately and isn't necessarily always in sync. This leads to some confusion and a lot of complexity when downloading/installing Magisk through the app. Starting from v22.0, the Magisk app (renamed from Magisk Manager) includes everything it needs within the APK itself, making installation a 100% offline process.
Custom recovery lovers, no worries! The Magisk app APK *itself* is a custom recovery flashable zip, just like MAGIC™🌈. Check out the updated [installation guide](https://topjohnwu.github.io/Magisk/install.html) for more info.
### App Hiding
Another major breakthrough in this release is that devices lower than Android 9.0 can now also use the advanced app hiding technique to hide the Magisk app. Due to this incompatible change, **RESTORE THE EXISTING MAGISK MANAGER BACK TO NORMAL BEFORE UPGRADING IF HIDDEN!**
### Bug Fixes
- [MagiskHide] Fix a bug when stopping MagiskHide does not take effect
- [MagiskBoot] Fix bug when unpacking `lz4_lg` compressed boot images
- [MagiskInit] Support Galaxy S21 series
- [MagiskSU] Fix incorrect APEX paths that caused `libsqlite.so` fail to load
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,22 +0,0 @@
## 2021.4.9 Magisk v22.1
This release is focused on fixing regressions and bugs. Check the [v22.0 release notes](https://topjohnwu.github.io/Magisk/releases/22000.html) if coming from older releases.
Note: Magisk v22 is the last major version to support Jellybean and Kitkat. Magisk v23 will only support Android 5.0 and higher.
### Bug Fixes
- [App] Prevent multiple installation sessions running in parallel
- [App] Prevent OutOfMemory crashes when checking boot signature on PXA boot images
- [General] Proper cgroup migration implementation
- [General] Rewrite log writer from scratch, should resolve any crashes and deadlocks
- [General] Many scripts updates fixing regressions
- [MagiskHide] Prevent possible deadlock when signal arrives
- [MagiskHide] Partial match process names if necessary
- [MagiskBoot] Preserve and patch AVB 2.0 structures/headers in boot images
- [MagiskBoot] Properly strip out data encryption flags
- [MagiskBoot] Prevent possible integer overflow
- [MagiskInit] Fix `sepolicy.rule` mounting strategy
- [resetprop] Always delete existing `ro.` props before updating. This will fix bootloops that could be caused by modifying device fingerprint properties.
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,21 +0,0 @@
## 2021.5.12 Magisk v23.0
This release is focused on fixing regressions and bugs.
Note: Magisk v22 is the last major version to support Jellybean and Kitkat. Magisk v23 only supports Android 5.0 and higher.
### Bug Fixes
- [App] Update snet extension. This fixes SafetyNet API errors.
- [App] Fix a bug in the stub app that causes APK installation to fail
- [App] Hide annoying errors in logs when hidden as stub
- [App] Fix issues when patching ODIN tar files when the app is hidden
- [General] Remove all pre Android 5.0 support
- [General] Update BusyBox to use proper libc
- [General] Fix C++ undefined behaviors
- [General] Several `sepolicy.rule` copy/installation fixes
- [MagiskPolicy] Remove unnecessary sepolicy rules
- [MagiskHide] Update package and process name validation logic
- [MagiskHide] Some changes that prevents zygote deadlock
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,21 +0,0 @@
## 2022.1.26 Magisk v24.0
It has been a while since the last public release, long time no see! A personal update for those unaware: I am now working at Google on the Android Platform Security team. Without further ado, let's jump right into it!
### MagiskHide Removal
I have lost interest in fighting this battle for quite a while; plus, the existing MagiskHide implementation is flawed in so many ways. Decoupling Magisk from root hiding is, in my opinion, beneficial to the community. Ever since my announcement on Twitter months ago, highly effective "root hiding" modules (much **MUCH** better than MagiskHide) has been flourishing, which again shows that people are way more capable than I am on this subject. So why not give those determined their time to shine, and let me focus on improving Magisk instead of drowning in the everlasting cat-and-mouse game 😉.
### Sunsetting Magisk-Modules-Repo
Due to lack of time and maintenance, the centralized Magisk-Modules-Repo was frozen, and the functionality to download modules from the repo is removed in v24.0. As a supplement, module developers can now specify an `updateJson` URL in their modules. The Magisk app will use that to check, download, and install module updates.
### Introducing Zygisk
Zygisk is **Magisk in Zygote**, the next big thing for Magisk! When this feature is enabled, a part of Magisk will run in the `Zygote` daemon process, allowing module developers to run code directly in every Android apps' processes. If you've heard of [Riru](https://github.com/RikkaApps/Riru), then Zygisk is inspired by that project and is functionally similar, though the implementation is quite different internally. I cannot wait to see what module developers can achieve using Zygisk!
### Documentation
For developers, details about `updateJson` and building Zygisk modules can all be found in the updated [documentation](https://topjohnwu.github.io/Magisk/guides.html#magisk-modules).
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,23 +0,0 @@
## 2022.1.28 Magisk v24.1
> For those coming from v24.0, v24.1 only has some minor app improvements. The following are copied from v24.0 release notes.
It has been a while since the last public release, long time no see! A personal update for those unaware: I am now working at Google on the Android Platform Security team. Without further ado, let's jump right into it!
### MagiskHide Removal
I have lost interest in fighting this battle for quite a while; plus, the existing MagiskHide implementation is flawed in so many ways. Decoupling Magisk from root hiding is, in my opinion, beneficial to the community. Ever since my announcement on Twitter months ago, highly effective "root hiding" modules (much **MUCH** better than MagiskHide) has been flourishing, which again shows that people are way more capable than I am on this subject. So why not give those determined their time to shine, and let me focus on improving Magisk instead of drowning in the everlasting cat-and-mouse game 😉.
### Sunsetting Magisk-Modules-Repo
Due to lack of time and maintenance, the centralized Magisk-Modules-Repo was frozen, and the functionality to download modules from the repo is removed in v24.0. As a supplement, module developers can now specify an `updateJson` URL in their modules. The Magisk app will use that to check, download, and install module updates.
### Introducing Zygisk
Zygisk is **Magisk in Zygote**, the next big thing for Magisk! When this feature is enabled, a part of Magisk will run in the `Zygote` daemon process, allowing module developers to run code directly in every Android apps' processes. If you've heard of [Riru](https://github.com/RikkaApps/Riru), then Zygisk is inspired by that project and is functionally similar, though the implementation is quite different internally. I cannot wait to see what module developers can achieve using Zygisk!
### Documentation
For developers, details about `updateJson` and building Zygisk modules can all be found in the updated [documentation](https://topjohnwu.github.io/Magisk/guides.html#magisk-modules).
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,21 +0,0 @@
## 2022.3.1 Magisk v24.2
Maintenance release fixing various issues.
- [MagiskSU] Fix buffer overflow
- [MagiskSU] Fix owner managed multiuser superuser settings
- [MagiskSU] Fix command logging when using `su -c <cmd>`
- [MagiskSU] Prevent su request indefinite blocking
- [MagiskBoot] Support `lz4_legacy` archive with multiple magic
- [MagiskBoot] Fix `lz4_lg` compression
- [DenyList] Allow targeting processes running as system UID
- [Zygisk] Workaround Samsung's "early zygote"
- [Zygisk] Improved Zygisk loading mechanism
- [Zygisk] Fix application UID tracking
- [Zygisk] Fix improper `umask` being set in zygote
- [App] Fix BusyBox execution test
- [App] Improve stub loading mechanism
- [App] Major app upgrade flow improvements
- [General] Improve commandline error handling and messaging
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,9 +0,0 @@
## 2022.3.10 Magisk v24.3
For those coming from v24.1, check the full changelog for changes introduced in v24.2.
- [General] Stop using `getrandom` syscall
- [Zygisk] Update API to v3, adding new fields to `AppSpecializeArgs`
- [App] Improve app repackaging installation workflow
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,23 +0,0 @@
## 2022.6.7 Magisk v25.0
Another major release! A lot of the changes aren't visible at the surface, but v25 is actually a really substantial upgrade!
### MagiskInit Rewrite
A significant portion of `magiskinit` (the critical software that runs before your device boots up) is completely rewritten from scratch. Ever since Android introduced [Project Treble](https://android-developers.googleblog.com/2017/05/here-comes-treble-modular-base-for.html) in Android 8.0, Magisk has been constantly fighting against the increasingly complex partitioning and early mount setups of all kinds of devices, sometimes with weird OEM specific implementations. It got to a point that `magiskinit` had become so complicated that few people (including myself!) were aware of every detail, and maintaining this piece of software like this was clearly not sustainable. After many months of planning (yes, this whole re-architecture has been in my head for a long time) and some help from external contributors, a whole new `sepolicy` injection mechanism is introduced into Magisk, solving the "SELinux Problem" once and for all.
Since this is a full paradigm shift on how Magisk hot-patch the device at boot, several behaviors that many developers implicitly relied on might not exist. For example, Magisk no longer patches fstabs in most scenarios, which means AVB will remain intact; some custom kernels rely on AVB being stripped out for them by Magisk.
### MagiskSU Security Enhancements
The superuser functionality of Magisk has not seen much changes ever since its introduction. v25 focuses on making root permission management more accurate and secure:
- Add a whole new package tracking system to ensure malicious UID reuse attack cannot be performed
- Properly support and implement the UX in the Magisk app for packages using `sharedUserId`
- Enforce root manager APK signature verification to combat the rampant unofficial Magisk app "mods"
Many might not realize, but using a trusted, unmodified Magisk app is really important. Magisk's root daemon treats the Magisk app differently and gives it blanket root access without any restrictions. A modded Magisk app can potentially backdoor your device.
And in case some of you are about to put on your tin foil hats, this is not designed to "vendor lock-in"; the goal is to make sure your root management app comes from the same developer of the underlying root implementation. Magisk's build system allows custom distributors to use its own signing keys, and in addition, I am also providing official debug builds which skips any signature verification for development.
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,25 +0,0 @@
## 2022.6.19 Magisk v25.1
> v25.1 fixes some minor bugs over v25.0. The following are the same as v25.0 release notes.
Another major release! A lot of the changes aren't visible at the surface, but v25 is actually a really substantial upgrade!
### MagiskInit Rewrite
A significant portion of `magiskinit` (the critical software that runs before your device boots up) is completely rewritten from scratch. Ever since Android introduced [Project Treble](https://android-developers.googleblog.com/2017/05/here-comes-treble-modular-base-for.html) in Android 8.0, Magisk has been constantly fighting against the increasingly complex partitioning and early mount setups of all kinds of devices, sometimes with weird OEM specific implementations. It got to a point that `magiskinit` had become so complicated that few people (including myself!) were aware of every detail, and maintaining this piece of software like this was clearly not sustainable. After many months of planning (yes, this whole re-architecture has been in my head for a long time) and some help from external contributors, a whole new `sepolicy` injection mechanism is introduced into Magisk, solving the "SELinux Problem" once and for all.
Since this is a full paradigm shift on how Magisk hot-patch the device at boot, several behaviors that many developers implicitly relied on might not exist. For example, Magisk no longer patches fstabs in most scenarios, which means AVB will remain intact; some custom kernels rely on AVB being stripped out for them by Magisk.
### MagiskSU Security Enhancements
The superuser functionality of Magisk has not seen much changes ever since its introduction. v25 focuses on making root permission management more accurate and secure:
- Add a whole new package tracking system to ensure malicious UID reuse attack cannot be performed
- Properly support and implement the UX in the Magisk app for packages using `sharedUserId`
- Enforce root manager APK signature verification to combat the rampant unofficial Magisk app "mods"
Many might not realize, but using a trusted, unmodified Magisk app is really important. Magisk's root daemon treats the Magisk app differently and gives it blanket root access without any restrictions. A modded Magisk app can potentially backdoor your device.
And in case some of you are about to put on your tin foil hats, this is not designed to "vendor lock-in"; the goal is to make sure your root management app comes from the same developer of the underlying root implementation. Magisk's build system allows custom distributors to use its own signing keys, and in addition, I am also providing official debug builds which skips any signature verification for development.
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,11 +0,0 @@
## 2022.7.20 Magisk v25.2
Maintenance release fixing various issues.
- [MagiskInit] Fix a potential issue when stub cpio is used
- [MagiskInit] Fix reboot to recovery when stub cpio is used
- [MagiskInit] Fix sepolicy.rules symlink for rootfs devices
- [General] Better data encryption detection
- [General] Move the whole logging infrastructure into Rust
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,25 +0,0 @@
## 2023.4.5 Magisk v26.0
Hey! Long time no see!
### Bumping Minimum Android Version to 6.0
Magisk's support for Android Lollipop has been pretty broken for a while without it being noticed. Also, none of the active developers of Magisk have actual hardware to run Android Lollipop. We rely on using the official Android emulator for regression testing on older platforms, however Google never shipped a Lollipop emulator image with SELinux support, leaving us with no option but to drop Lollipop support since we don't feel comfortable supporting Android Lollipop without adequate testing.
### New Magic Mount Implementation
Magic Mount, the feature that make modules modify partitions, has gone through a major rewrite. The existing implementation doesn't work well with OEMs injecting overlays into their system using `overlayfs`. The new implementation fundamentally changes how filesystem mirrors are created, giving us a more accurate clone of the unmodified filesystem.
### New `sepolicy.rule` Implementation
Magisk allows modules to provide custom SELinux patches by including the file `sepolicy.rule`. Due to the complicated nature of SELinux patching, the compatibility of this functionality has been pretty spotty; many devices are not supported. In this release, a brand new pre-init partition detection mechanism has been designed to support even more devices. Due to complicated reasons, this detection mechanism cannot be performed in a custom recovery environment.
**This means that any installation of Magisk v26+ using custom recovery will be incomplete; a subsequent re-installation through the Magisk app after booting up is required.**
### Zygisk Updates
**The new Zygisk API v4 is now live!** It comes with new features and a refined PLT function hook API. The implementaton of Zygisk has also gone through some major refactoring, including new code loading/unloading mechanisms and a new PLT function hook implementation.
Head over to the [Zygisk Module Sample](https://github.com/topjohnwu/zygisk-module-sample) repository to check out the new API and documentation!
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,33 +0,0 @@
## 2023.4.11 Magisk v26.1
### Changes from v26.0
- [App] Fix crashing when revoking root permissions
- [MagiskInit] Always prefer `ext4` partitions over `f2fs` when selecting the pre-init partition
- [General] Restore module files' context/owner/group from mirror. This is a regression introduced in v26.0
(The following is the same as v26.0 release notes)
Hey! Long time no see!
### Bumping Minimum Android Version to 6.0
Magisk's support for Android Lollipop has been pretty broken for a while without it being noticed. Also, none of the active developers of Magisk have actual hardware to run Android Lollipop. We rely on using the official Android emulator for regression testing on older platforms, however Google never shipped a Lollipop emulator image with SELinux support, leaving us with no option but to drop Lollipop support since we don't feel comfortable supporting Android Lollipop without adequate testing.
### New Magic Mount Implementation
Magic Mount, the feature that make modules modify partitions, has gone through a major rewrite. The existing implementation doesn't work well with OEMs injecting overlays into their system using `overlayfs`. The new implementation fundamentally changes how filesystem mirrors are created, giving us a more accurate clone of the unmodified filesystem.
### New `sepolicy.rule` Implementation
Magisk allows modules to provide custom SELinux patches by including the file `sepolicy.rule`. Due to the complicated nature of SELinux patching, the compatibility of this functionality has been pretty spotty; many devices are not supported. In this release, a brand new pre-init partition detection mechanism has been designed to support even more devices. Due to complicated reasons, this detection mechanism cannot be performed in a custom recovery environment.
**This means that any installation of Magisk v26+ using custom recovery will be incomplete; a subsequent re-installation through the Magisk app after booting up is required.**
### Zygisk Updates
**The new Zygisk API v4 is now live!** It comes with new features and a refined PLT function hook API. The implementaton of Zygisk has also gone through some major refactoring, including new code loading/unloading mechanisms and a new PLT function hook implementation.
Head over to the [Zygisk Module Sample](https://github.com/topjohnwu/zygisk-module-sample) repository to check out the new API and documentation!
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,19 +0,0 @@
## 2023.8.27 Magisk v26.2
- [MagiskBoot] Support extracting boot image from `payload.bin`
- [MagiskBoot] Support cpio files containing character files
- [MagiskBoot] Support listing cpio content
- [MagiskBoot] Directly handle AVB 1.0 signing and verification without going through Java implementation
- [Daemon] Make daemon socket a fixed path in MAGISKTMP
- [resetprop] Support printing property context
- [resetprop] Support only printing persistent properties from storage
- [resetprop] Properly support setting persistent properties bypassing property_service
- [MagiskSU] Support `-g` and `-G` options
- [MagiskSU] Support switching mount namespace to PID with `-t`
- [MagiskPolicy] Fix patching extended permissions
- [MagiskPolicy] Support more syntax for extended permissions
- [MagiskPolicy] Support printing out the loaded sepolicy rules
- [App] Support patching boot image from ROM zips
- [App] Properly preserve `boot.img` when patching Samsung firmware with `init_boot.img`
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,28 +0,0 @@
## 2023.9.4 Magisk v26.3
### v26.3
- [General] Fix device information detection script
- [General] Update BusyBox to 1.36.1
- [General] Update toolchain that produces broken arm32 executables
- [App] Fix root service unable to bind on OnePlus devices
### v26.2
- [MagiskBoot] Support extracting boot image from `payload.bin`
- [MagiskBoot] Support cpio files containing character files
- [MagiskBoot] Support listing cpio content
- [MagiskBoot] Directly handle AVB 1.0 signing and verification without going through Java implementation
- [Daemon] Make daemon socket a fixed path in MAGISKTMP
- [resetprop] Support printing property context
- [resetprop] Support only printing persistent properties from storage
- [resetprop] Properly support setting persistent properties bypassing property_service
- [MagiskSU] Support `-g` and `-G` options
- [MagiskSU] Support switching mount namespace to PID with `-t`
- [MagiskPolicy] Fix patching extended permissions
- [MagiskPolicy] Support more syntax for extended permissions
- [MagiskPolicy] Support printing out the loaded sepolicy rules
- [App] Support patching boot image from ROM zips
- [App] Properly preserve `boot.img` when patching Samsung firmware with `init_boot.img`
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,12 +0,0 @@
## 2023.11.5 Magisk v26.4
- [MagiskBoot] Don't pad zeros if signed boot image is larger
- [MagiskPolicy] Fix `genfscon` and `filename_trans`
- [MagiskPolicy] Fix bug in `libsepol`
- [Zygisk] Fix and simplify file descriptor sanitization logic
- [App] Prevent OOM when patching AP tarfiles
- [App] Fix bug in device configuration detection
- [Daemon] Fix certificate parsing of APKs
- [General] Fix logging errors from C++ code being ignored
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,9 +0,0 @@
## 2024.2.3 Magisk v27.0
- [Zygisk] Introduce new code injection mechanism
- [Zygisk] Support new signature introduced in U QPR2
- [SEPolicy] Update libsepol to properly set some policy config bits
- [MagiskBoot] Support compressing `init` so Magisk is installable on devices with small boot partitions
- [ResetProp] Add new wait for property feature `resetprop -w`
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,25 +0,0 @@
## 2024.10.10 Magisk v28.0
- [General] Support 16k page size
- [General] Add basic support for RISC-V (not built in releases)
- [General] Use a minimal libc to build static executables (`magiskinit` and `magiskboot`) for smaller sizes
- [Core] Remove unnecessary mirror for magic mount
- [Core] Update boot image detection logic to support more devices
- [MagiskInit] Rewrite 2SI logic for injecting `magiskinit` as `init`
- [MagiskInit] Update preinit partition detection
- [Zygisk] Update internal JNI hooking implementation
- [MagiskPolicy] Preserve sepolicy config flag after patching
- [MagiskPolicy] Optimize patching rules to reduce the amount of new rules being injected
- [DenyList] Support enforcing denylist when Zygisk is disabled
- [Resetprop] Improve implementation to workaround several property modification detections
- [Resetprop] Update to properly work with property overlays
- [App] Major internal code refactoring
- [App] Support patching Samsung firmware with images larger than 8GiB
- [App] Use user-initiated job instead of foreground services on Android 14
- [App] Support Android 13+ built-in per-app language preferences
- [App] Add `action.sh` support to allow modules to define an action triggered from UI
- [MagiskBoot] Support spliting kernel images without decompression
- [MagiskBoot] Properly support vendor boot images
- [MagiskBoot] Disable Samsung PROCA from kernel image
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,33 +0,0 @@
## 2024.12.6 Magisk v28.1
- [App] Fix stub APK download link
- [App] Fix support for Android lower than 8.0
- [General] Fix support for MTK Samsung devices
- [MagiskInit] Fix a regression for 2SI devices
- [MagiskPolicy] Fix a regression causing `overlay.d` replaced files to be not accessible
## Magisk v28.0 Changes
- [General] Support 16k page size
- [General] Add basic support for RISC-V (not built in releases)
- [General] Use a minimal libc to build static executables (`magiskinit` and `magiskboot`) for smaller sizes
- [Core] Remove unnecessary mirror for magic mount
- [Core] Update boot image detection logic to support more devices
- [MagiskInit] Rewrite 2SI logic for injecting `magiskinit` as `init`
- [MagiskInit] Update preinit partition detection
- [Zygisk] Update internal JNI hooking implementation
- [MagiskPolicy] Preserve sepolicy config flag after patching
- [MagiskPolicy] Optimize patching rules to reduce the amount of new rules being injected
- [DenyList] Support enforcing denylist when Zygisk is disabled
- [Resetprop] Improve implementation to workaround several property modification detections
- [Resetprop] Update to properly work with property overlays
- [App] Major internal code refactoring
- [App] Support patching Samsung firmware with images larger than 8GiB
- [App] Use user-initiated job instead of foreground services on Android 14
- [App] Support Android 13+ built-in per-app language preferences
- [App] Add `action.sh` support to allow modules to define an action triggered from UI
- [MagiskBoot] Support spliting kernel images without decompression
- [MagiskBoot] Properly support vendor boot images
- [MagiskBoot] Disable Samsung PROCA from kernel image
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,16 +0,0 @@
## 2025.5.14 Magisk v29.0
This release looks minor at the surface, however, the entire codebase has gone through significant refactoring and migration. The native code in Magisk used to be mainly C++, but several contributors and I have been steadily rewriting parts of the code in Rust since April 2022. After years of effort, the Rust-ification of the project slowly began picking up steam, and at the moment of this release, over 40% of the native code has been rewritten in Rust, with several major subsystem rewrites in the PR queue, planned to be merged for the next release.
Many might wonder, why introduce a new language to the project? My reason is actually not to reduce memory safety issues (although it is a nice side benefit), but to be able to develop Magisk using a more modern programming language. After using Rust for a while, it's clear to me that using Rust allows me to write more correct code and makes me happier compared to dealing with C++. People share the [same sentiment as I do](https://threadreaderapp.com/thread/1577667445719912450.html).
## Changelog
- [General] Massive internal refactoring and code migration
- [App] Support downloading module zip files with XZ compression
- [App] Disable app animations when system animations are disabled
- [MagiskMount] Support systemlessly deleting files with modules using blank file nodes
- [MagiskInit] Redesign sepolicy patching and injection logic
- [MagiskSU] Better TTY/PTY support
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,37 +0,0 @@
# Release Notes
- [v29.0](29000.md)
- [v28.1](28100.md)
- [v28.0](28000.md)
- [v27.0](27000.md)
- [v26.4](26400.md)
- [v26.3](26300.md)
- [v26.2](26200.md)
- [v26.1](26100.md)
- [v26.0](26000.md)
- [v25.2](25200.md)
- [v25.1](25100.md)
- [v25.0](25000.md)
- [v24.3](24300.md)
- [v24.2](24200.md)
- [v24.1](24100.md)
- [v24.0](24000.md)
- [v23.0](23000.md)
- [v22.1](22100.md)
- [v22.0](22000.md)
- [v21.4](21400.md)
- [v21.2](21200.md)
- [v21.1](21100.md)
- [v21.0](21000.md)
- [v20.4](20400.md)
- [v20.3](20300.md)
- [v20.2](20200.md)
- [v20.1](20100.md)
- [v20.0](20000.md)
- [v19.4](19400.md)
- [v19.3](19300.md)
- [v19.2](19200.md)
- [v19.1](19100.md)
- [v19.0](19000.md)
- [v18.1](18100.md)
- [v18.0](18000.md)

View File

@@ -20,12 +20,10 @@ LOCAL_SRC_FILES := \
core/daemon.cpp \
core/scripting.cpp \
core/sqlite.cpp \
core/module.cpp \
core/thread.cpp \
core/core-rs.cpp \
core/resetprop/resetprop.cpp \
core/su/su.cpp \
core/su/connect.cpp \
core/zygisk/entry.cpp \
core/zygisk/module.cpp \
core/zygisk/hook.cpp \
@@ -88,9 +86,7 @@ LOCAL_STATIC_LIBRARIES := \
libboot-rs
LOCAL_SRC_FILES := \
boot/main.cpp \
boot/bootimg.cpp \
boot/format.cpp \
boot/boot-rs.cpp
LOCAL_LDFLAGS := -static

135
native/src/Cargo.lock generated
View File

@@ -45,9 +45,9 @@ checksum = "a464143cc82dedcdc3928737445362466b7674b5db4e2eb8e869846d6d84f4f6"
[[package]]
name = "autocfg"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "base"
@@ -109,9 +109,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.18.1"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "bytemuck"
@@ -124,9 +124,9 @@ dependencies = [
[[package]]
name = "bytemuck_derive"
version = "1.9.3"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4"
dependencies = [
"proc-macro2",
"quote",
@@ -141,29 +141,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bzip2"
version = "0.5.2"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47"
checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff"
dependencies = [
"bzip2-sys",
"libbz2-rs-sys",
]
[[package]]
name = "bzip2-sys"
version = "0.1.13+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "cc"
version = "1.2.26"
version = "1.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac"
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
dependencies = [
"shlex",
]
@@ -176,18 +165,18 @@ checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "clap"
version = "4.5.40"
version = "4.5.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.40"
version = "4.5.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966"
dependencies = [
"anstyle",
"clap_lex",
@@ -247,18 +236,18 @@ dependencies = [
[[package]]
name = "crc32fast"
version = "1.4.2"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-bigint"
version = "0.7.0-pre.4"
version = "0.7.0-pre.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edaae5fb9dac79a07260e0b2006799ff4f1d342ab243fd7d0892215113b27904"
checksum = "98dc20cae677f0af161d98f18463804b680f9af060f6dbe6d4249bd7e838bca1"
dependencies = [
"hybrid-array",
"num-traits",
@@ -336,9 +325,9 @@ dependencies = [
[[package]]
name = "der"
version = "0.8.0-rc.4"
version = "0.8.0-rc.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2e6107818886eff6b71fba7a2da3dd11025ebb80f0c9b94ff961168ef629f2"
checksum = "e2fe0a4fafae25053c19a03fefe040607bda956b4941d692ed9fb9d3c18a3193"
dependencies = [
"const-oid",
"der_derive",
@@ -381,9 +370,9 @@ dependencies = [
[[package]]
name = "ecdsa"
version = "0.17.0-rc.1"
version = "0.17.0-rc.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ca18d8009d96ffc2a8b771c7432338233ffcfa05e4ca410ed77900a2a335a0b"
checksum = "112839e868b3376c2066506d42331023165d687a7ed38b2ed77f28763d9a7742"
dependencies = [
"der",
"digest",
@@ -396,9 +385,9 @@ dependencies = [
[[package]]
name = "elliptic-curve"
version = "0.14.0-rc.5"
version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "541598dba361b5ba0321caad955ba99ae82a604f4047c4f2743724996abf62f4"
checksum = "c28ecec37eea07ab976cea93c7ce8b36d561cf161f6767925c1edc51024b0ad3"
dependencies = [
"base16ct",
"crypto-bigint",
@@ -497,15 +486,15 @@ dependencies = [
[[package]]
name = "libbz2-rs-sys"
version = "0.1.3"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0864a00c8d019e36216b69c2c4ce50b83b7bd966add3cf5ba554ec44f8bebcf5"
checksum = "775bf80d5878ab7c2b1080b5351a48b2f737d9f6f8b383574eebcc22be0dfccb"
[[package]]
name = "libc"
version = "0.2.172"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "libm"
@@ -621,9 +610,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.7.4"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "minimal-lexical"
@@ -672,9 +661,9 @@ dependencies = [
[[package]]
name = "p256"
version = "0.14.0-pre.5"
version = "0.14.0-pre.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42c06f1f28ff328cb76c95cb7aebd6734a8333b98bdac393bdc124d16561dcb"
checksum = "1be97a30a85c829fdac914cebb89ef05e109f9e5eb6510f46f623be91bc39ded"
dependencies = [
"ecdsa",
"elliptic-curve",
@@ -685,9 +674,9 @@ dependencies = [
[[package]]
name = "p384"
version = "0.14.0-pre.5"
version = "0.14.0-pre.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7594e57ef1ce505538e5a8e3485a21b930e99701bb65c8ede899a3a8213174"
checksum = "ea9626bce3d0bf768a28778618e5095131cd32bfca5297b51bbcb4abe7fae62a"
dependencies = [
"ecdsa",
"elliptic-curve",
@@ -698,9 +687,9 @@ dependencies = [
[[package]]
name = "p521"
version = "0.14.0-pre.5"
version = "0.14.0-pre.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9396e2414ace7de7e0f3d544a5a07f129e39b28b2f08a35b3b7febdea36fd8e9"
checksum = "ec1071ec0ddc9a8c198692acf5620176b0cd9c1db988acef030e101f851405f4"
dependencies = [
"base16ct",
"ecdsa",
@@ -731,36 +720,29 @@ dependencies = [
[[package]]
name = "pkcs1"
version = "0.8.0-rc.2"
version = "0.8.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24e16d93c725fa250577ffdec06ebbff4cae3625b0e2881ac43a5427797ee8d3"
checksum = "b2345503b65d9be13aac96ddbec3eed60def8bc83869f9a519789afbcf3c2bea"
dependencies = [
"der",
"pkcs8",
"spki",
]
[[package]]
name = "pkcs8"
version = "0.11.0-rc.4"
version = "0.11.0-rc.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f1843d4345dfe1a55e487db747a04c01af50415b03e937410e0a41d8cc24ec7"
checksum = "c53e5d0804fa4070b1b2a5b320102f2c1c094920a7533d5d87c2630609bcbd34"
dependencies = [
"der",
"spki",
]
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "primefield"
version = "0.14.0-pre.2"
version = "0.14.0-pre.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bbeb92947a0d0d4b0cab5e2e6749acc44c81461eb3b1aff4dbb7acd0eb9f0ab"
checksum = "adc85f9f75dc05486f61bc61858535c0501a0ca81ca3117ab17befbead13c110"
dependencies = [
"crypto-bigint",
"ff",
@@ -771,9 +753,9 @@ dependencies = [
[[package]]
name = "primeorder"
version = "0.14.0-pre.4"
version = "0.14.0-pre.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "979936340c6e8b108ad132b395a1682f02a0b179080ed3380320c2c888728429"
checksum = "af12dd34fc62d04416de85af032f4595369437fb7b0143d36ae60cecaf5cdddf"
dependencies = [
"elliptic-curve",
]
@@ -806,9 +788,9 @@ dependencies = [
[[package]]
name = "r-efi"
version = "5.2.0"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand_core"
@@ -831,9 +813,9 @@ dependencies = [
[[package]]
name = "rsa"
version = "0.10.0-rc.0"
version = "0.10.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30f0ad781aea19fe741d7a901b2ad8b4271ac3516e7045b8ecff74e201968fe"
checksum = "7e8cb237ca3624409eda7d73de0d423815c9d91175ed5a62a8dd6549d2408cc2"
dependencies = [
"const-oid",
"crypto-bigint",
@@ -863,14 +845,13 @@ checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]]
name = "sec1"
version = "0.8.0-rc.5"
version = "0.8.0-rc.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4855dd9b15e8e469fad23529698f7f7b7a6b250a81c88b1f9d7efe1abca7717"
checksum = "c54dee398d74b1d03d78ddc09c90e456bf906b5b7aa790ba4f48b025b2179e5d"
dependencies = [
"base16ct",
"der",
"hybrid-array",
"pkcs8",
"subtle",
"zeroize",
]
@@ -935,9 +916,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signature"
version = "3.0.0-rc.1"
version = "3.0.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8852cecbd17ba45978bbbe43061ebe36a2ae376058c5c172e09f72888f8f7de"
checksum = "4835c3b5ecb10171941a4998a95a3a76ecac1c5ae8e6954f2ad030acd1c7e8ab"
dependencies = [
"digest",
"rand_core",
@@ -957,9 +938,9 @@ checksum = "1b6709c7b6754dca1311b3c73e79fcce40dd414c782c66d88e8823030093b02b"
[[package]]
name = "spki"
version = "0.8.0-rc.2"
version = "0.8.0-rc.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2f0e2bdca9b00f5be6dd3bb6647d50fd0f24a508a95f78e3bb2fe98d0403c85"
checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80"
dependencies = [
"base64ct",
"der",
@@ -979,9 +960,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.102"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
@@ -1164,9 +1145,9 @@ dependencies = [
[[package]]
name = "x509-cert"
version = "0.3.0-rc.0"
version = "0.3.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5c645061d1dc562a65edda59c7f688f35403a4615adfc07460437442d6e8383"
checksum = "015655a524ab1db259a56aaaec614cf84952299f4081c2602881d556e2ed2838"
dependencies = [
"const-oid",
"der",

View File

@@ -10,7 +10,7 @@ edition = "2024"
[workspace.dependencies]
cxx = { path = "external/cxx-rs" }
cxx-gen = { path = "external/cxx-rs/gen/lib" }
libc = "0.2.172"
libc = "0.2.174"
cfg-if = "1.0.1"
num-traits = "0.2.19"
num-derive = "0.4.2"
@@ -21,14 +21,14 @@ bytemuck = "1.23.1"
fdt = "0.1.5"
const_format = "0.2.34"
bit-set = "0.8.0"
syn = "2.0.102"
syn = "2.0.104"
quote = "1.0.40"
proc-macro2 = "1.0.95"
argh = { version = "0.1.13", default-features = false }
pb-rs = { version = "0.10.0", default-features = false }
quick-protobuf = "0.8.1"
flate2 = { version = "1.1.2", default-features = false }
bzip2 = { version = "0.5.2", default-features = false }
bzip2 = { version = "0.6.0" }
zopfli = "0.8.2"
lz4 = "1.28.1"
xz2 = "0.1.7"
@@ -37,12 +37,12 @@ xz2 = "0.1.7"
sha1 = "0.11.0-rc.0"
sha2 = "0.11.0-rc.0"
digest = "0.11.0-rc.0"
p256 = "0.14.0-pre.5"
p384 = "0.14.0-pre.5"
p521 = "0.14.0-pre.5"
rsa = "0.10.0-rc.0"
x509-cert = "0.3.0-rc.0"
der = "0.8.0-rc.4"
p256 = "0.14.0-pre.9"
p384 = "0.14.0-pre.9"
p521 = "0.14.0-pre.9"
rsa = "0.10.0-rc.3"
x509-cert = "0.3.0-rc.1"
der = "0.8.0-rc.7"
[patch.crates-io]
pb-rs = { git = "https://github.com/tafia/quick-protobuf.git" }

View File

@@ -300,6 +300,13 @@ impl Utf8CStr {
}
}
pub unsafe fn from_raw_parts<'a>(ptr: *const c_char, len: usize) -> &'a Utf8CStr {
unsafe {
let bytes = slice::from_raw_parts(ptr.cast(), len);
Self::from_bytes_unchecked(bytes)
}
}
#[inline(always)]
pub fn as_bytes_with_nul(&self) -> &[u8] {
&self.0

View File

@@ -309,7 +309,7 @@ impl Directory {
self.pre_order_walk_impl(&mut f)
}
pub fn remove_all(&mut self) -> OsResultStatic<()> {
pub fn remove_all(mut self) -> OsResultStatic<()> {
self.post_order_walk(|e| {
e.unlink()?;
Ok(WalkResult::Continue)

View File

@@ -140,8 +140,14 @@ pub struct FileAttr {
pub con: crate::Utf8CStrBufArr<128>,
}
impl Default for FileAttr {
fn default() -> Self {
Self::new()
}
}
impl FileAttr {
fn new() -> Self {
pub fn new() -> Self {
FileAttr {
st: unsafe { mem::zeroed() },
#[cfg(feature = "selinux")]
@@ -227,7 +233,7 @@ impl Utf8CStr {
pub fn remove_all(&self) -> OsResultStatic<()> {
let attr = self.get_attr()?;
if attr.is_dir() {
let mut dir = Directory::try_from(open_fd(self, O_RDONLY | O_CLOEXEC, 0)?)?;
let dir = Directory::try_from(open_fd(self, O_RDONLY | O_CLOEXEC, 0)?)?;
dir.remove_all()?;
}
Ok(self.remove()?)
@@ -583,7 +589,7 @@ impl<S: Utf8CStrBuf + Sized> FsPathBuilder for S {
}
fn append_path_fmt<T: Display>(&mut self, name: T) -> &mut Self {
self.write_fmt(format_args!("/{}", name)).ok();
self.write_fmt(format_args!("/{name}")).ok();
self
}
}
@@ -595,7 +601,7 @@ impl FsPathBuilder for dyn Utf8CStrBuf + '_ {
}
fn append_path_fmt<T: Display>(&mut self, name: T) -> &mut Self {
self.write_fmt(format_args!("/{}", name)).ok();
self.write_fmt(format_args!("/{name}")).ok();
self
}
}
@@ -846,7 +852,7 @@ fn parse_mount_info_line(line: &str) -> Option<MountInfo> {
pub fn parse_mount_info(pid: &str) -> Vec<MountInfo> {
let mut res = vec![];
let mut path = format!("/proc/{}/mountinfo", pid);
let mut path = format!("/proc/{pid}/mountinfo");
if let Ok(file) = Utf8CStr::from_string(&mut path).open(O_RDONLY | O_CLOEXEC) {
BufReader::new(file).foreach_lines(|line| {
parse_mount_info_line(line)

View File

@@ -184,6 +184,10 @@ int parse_int(string_view s) {
return parse_num<int, 10>(s);
}
uint32_t parse_uint32_hex(string_view s) {
return parse_num<uint32_t, 16>(s);
}
int switch_mnt_ns(int pid) {
int ret = -1;
int fd = syscall(__NR_pidfd_open, pid, 0);

View File

@@ -175,6 +175,7 @@ rust::Vec<size_t> mut_u8_patch(
rust::Slice<const uint8_t> from,
rust::Slice<const uint8_t> to);
uint32_t parse_uint32_hex(std::string_view s);
int parse_int(std::string_view s);
using thread_entry = void *(*)(void *);

View File

@@ -1,13 +1,16 @@
use crate::{StrErr, Utf8CStr, ffi};
use crate::{Utf8CStr, cstr, ffi};
use argh::EarlyExit;
use libc::c_char;
use std::fmt::Arguments;
use std::io::Write;
use std::mem::ManuallyDrop;
use std::process::exit;
use std::sync::Arc;
use std::sync::atomic::{AtomicPtr, Ordering};
use std::{fmt, slice, str};
use std::{
fmt,
fmt::Arguments,
io::Write,
mem::ManuallyDrop,
process::exit,
slice, str,
sync::Arc,
sync::atomic::{AtomicPtr, Ordering},
};
pub fn errno() -> &'static mut i32 {
unsafe { &mut *libc::__errno() }
@@ -76,15 +79,6 @@ impl<T: AsMut<[u8]> + ?Sized> MutBytesExt for T {
}
}
// SAFETY: libc guarantees argc and argv are properly setup and are static
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn map_args(argc: i32, argv: *const *const c_char) -> Result<Vec<&'static str>, StrErr> {
unsafe { slice::from_raw_parts(argv, argc as usize) }
.iter()
.map(|s| unsafe { Utf8CStr::from_ptr(*s) }.map(|s| s.as_str()))
.collect()
}
pub trait EarlyExitExt<T> {
fn on_early_exit<F: FnOnce()>(self, print_help_msg: F) -> T;
}
@@ -99,7 +93,7 @@ impl<T> EarlyExitExt<T> for Result<T, EarlyExit> {
exit(0)
}
Err(_) => {
eprintln!("{}", output);
eprintln!("{output}");
print_help_msg();
exit(1)
}
@@ -227,3 +221,35 @@ impl Chunker {
chunk
}
}
pub struct CmdArgs(pub Vec<&'static str>);
impl CmdArgs {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn new(argc: i32, argv: *const *const c_char) -> CmdArgs {
CmdArgs(
// SAFETY: libc guarantees argc and argv are properly setup and are static
unsafe { slice::from_raw_parts(argv, argc as usize) }
.iter()
.map(|s| unsafe { Utf8CStr::from_ptr(*s) })
.map(|r| r.unwrap_or(cstr!("<invalid>")))
.map(Utf8CStr::as_str)
.collect(),
)
}
pub fn as_slice(&self) -> &[&'static str] {
self.0.as_slice()
}
pub fn iter(&self) -> slice::Iter<'_, &'static str> {
self.0.iter()
}
pub fn cstr_iter(&self) -> impl Iterator<Item = &'static Utf8CStr> {
// SAFETY: libc guarantees null terminated strings
self.0
.iter()
.map(|s| unsafe { Utf8CStr::from_raw_parts(s.as_ptr().cast(), s.len() + 1) })
}
}

View File

@@ -23,32 +23,29 @@ pub type LoggedResult<T> = Result<T, LoggedError>;
#[macro_export]
macro_rules! log_err {
() => {{
Err($crate::LoggedError::default())
}};
($($args:tt)+) => {{
$crate::error!($($args)+);
$crate::LoggedError::default()
Err($crate::LoggedError::default())
}};
}
// Any result or option can be silenced
pub trait SilentResultExt<T> {
pub trait SilentLogExt<T> {
fn silent(self) -> LoggedResult<T>;
}
impl<T, E> SilentResultExt<T> for Result<T, E> {
impl<T, E> SilentLogExt<T> for Result<T, E> {
fn silent(self) -> LoggedResult<T> {
match self {
Ok(v) => Ok(v),
Err(_) => Err(LoggedError::default()),
}
self.map_err(|_| LoggedError::default())
}
}
impl<T> SilentResultExt<T> for Option<T> {
impl<T> SilentLogExt<T> for Option<T> {
fn silent(self) -> LoggedResult<T> {
match self {
Some(v) => Ok(v),
None => Err(LoggedError::default()),
}
self.ok_or_else(LoggedError::default)
}
}
@@ -64,142 +61,170 @@ pub(crate) trait CxxResultExt<T> {
fn log_cxx(self) -> LoggedResult<T>;
}
trait Loggable<T> {
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedResult<T>;
// Public API for converting Option to LoggedResult
pub trait OptionExt<T> {
fn ok_or_log(self) -> LoggedResult<T>;
fn ok_or_log_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T>;
}
impl<T> OptionExt<T> for Option<T> {
#[inline(always)]
fn ok_or_log(self) -> LoggedResult<T> {
self.ok_or_else(LoggedError::default)
}
#[cfg(not(debug_assertions))]
fn ok_or_log_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
self.ok_or_else(|| {
do_log_msg(LogLevel::Error, None, f);
LoggedError::default()
})
}
#[track_caller]
#[cfg(debug_assertions)]
fn ok_or_log_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
let caller = Some(Location::caller());
self.ok_or_else(|| {
do_log_msg(LogLevel::Error, caller, f);
LoggedError::default()
})
}
}
trait Loggable {
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedError;
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
self,
level: LogLevel,
caller: Option<&'static Location>,
f: F,
) -> LoggedResult<T>;
) -> LoggedError;
}
impl<T, R: Loggable<T>> CxxResultExt<T> for R {
impl<T, E: Loggable> CxxResultExt<T> for Result<T, E> {
fn log_cxx(self) -> LoggedResult<T> {
self.do_log(LogLevel::ErrorCxx, None)
self.map_err(|e| e.do_log(LogLevel::ErrorCxx, None))
}
}
impl<T, R: Loggable<T>> ResultExt<T> for R {
impl<T, E: Loggable> ResultExt<T> for Result<T, E> {
#[cfg(not(debug_assertions))]
fn log(self) -> LoggedResult<T> {
self.do_log(LogLevel::Error, None)
self.map_err(|e| e.do_log(LogLevel::Error, None))
}
#[track_caller]
#[cfg(debug_assertions)]
fn log(self) -> LoggedResult<T> {
self.do_log(LogLevel::Error, Some(Location::caller()))
let caller = Some(Location::caller());
self.map_err(|e| e.do_log(LogLevel::Error, caller))
}
#[cfg(not(debug_assertions))]
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
self.do_log_msg(LogLevel::Error, None, f)
self.map_err(|e| e.do_log_msg(LogLevel::Error, None, f))
}
#[track_caller]
#[cfg(debug_assertions)]
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
self.do_log_msg(LogLevel::Error, Some(Location::caller()), f)
let caller = Some(Location::caller());
self.map_err(|e| e.do_log_msg(LogLevel::Error, caller, f))
}
#[cfg(not(debug_assertions))]
fn log_ok(self) {
self.log().ok();
self.map_err(|e| e.do_log(LogLevel::Error, None)).ok();
}
#[track_caller]
#[cfg(debug_assertions)]
fn log_ok(self) {
self.do_log(LogLevel::Error, Some(Location::caller())).ok();
let caller = Some(Location::caller());
self.map_err(|e| e.do_log(LogLevel::Error, caller)).ok();
}
}
impl<T> Loggable<T> for LoggedResult<T> {
fn do_log(self, _: LogLevel, _: Option<&'static Location>) -> LoggedResult<T> {
impl<T> ResultExt<T> for LoggedResult<T> {
fn log(self) -> LoggedResult<T> {
self
}
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
self,
level: LogLevel,
caller: Option<&'static Location>,
f: F,
) -> LoggedResult<T> {
match self {
Ok(v) => Ok(v),
Err(_) => {
log_with_formatter(level, |w| {
if let Some(caller) = caller {
write!(w, "[{}:{}] ", caller.file(), caller.line())?;
}
f(w)?;
w.write_char('\n')
});
Err(LoggedError::default())
}
}
#[cfg(not(debug_assertions))]
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
do_log_msg(LogLevel::Error, None, f);
self
}
#[track_caller]
#[cfg(debug_assertions)]
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
let caller = Some(Location::caller());
do_log_msg(LogLevel::Error, caller, f);
self
}
fn log_ok(self) {}
}
impl<T, E: Display> Loggable<T> for Result<T, E> {
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedResult<T> {
match self {
Ok(v) => Ok(v),
Err(e) => {
if let Some(caller) = caller {
log_with_args!(level, "[{}:{}] {:#}", caller.file(), caller.line(), e);
} else {
log_with_args!(level, "{:#}", e);
}
Err(LoggedError::default())
}
}
}
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
self,
level: LogLevel,
caller: Option<&'static Location>,
f: F,
) -> LoggedResult<T> {
match self {
Ok(v) => Ok(v),
Err(e) => {
log_with_formatter(level, |w| {
if let Some(caller) = caller {
write!(w, "[{}:{}] ", caller.file(), caller.line())?;
}
f(w)?;
writeln!(w, ": {:#}", e)
});
Err(LoggedError::default())
}
}
}
}
// Automatically convert all printable errors to LoggedError to support `?` operator
impl<T: Display> From<T> for LoggedError {
// Allow converting Loggable errors to LoggedError to support `?` operator
impl<T: Loggable> From<T> for LoggedError {
#[cfg(not(debug_assertions))]
fn from(e: T) -> Self {
log_with_args!(LogLevel::Error, "{:#}", e);
LoggedError::default()
e.do_log(LogLevel::Error, None)
}
#[track_caller]
#[cfg(debug_assertions)]
fn from(e: T) -> Self {
let caller = Location::caller();
log_with_args!(
LogLevel::Error,
"[{}:{}] {:#}",
caller.file(),
caller.line(),
e
);
let caller = Some(Location::caller());
e.do_log(LogLevel::Error, caller)
}
}
// Actual logging implementation
// Make all printable objects Loggable
impl<T: Display> Loggable for T {
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedError {
if let Some(caller) = caller {
log_with_args!(level, "[{}:{}] {:#}", caller.file(), caller.line(), self);
} else {
log_with_args!(level, "{:#}", self);
}
LoggedError::default()
}
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
self,
level: LogLevel,
caller: Option<&'static Location>,
f: F,
) -> LoggedError {
log_with_formatter(level, |w| {
if let Some(caller) = caller {
write!(w, "[{}:{}] ", caller.file(), caller.line())?;
}
f(w)?;
writeln!(w, ": {self:#}")
});
LoggedError::default()
}
}
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
level: LogLevel,
caller: Option<&'static Location>,
f: F,
) {
log_with_formatter(level, |w| {
if let Some(caller) = caller {
write!(w, "[{}:{}] ", caller.file(), caller.line())?;
}
f(w)?;
w.write_char('\n')
});
}
// Check libc return value and map to Result
@@ -366,7 +391,7 @@ impl Display for OsError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let error = self.as_io_error();
if self.name.is_empty() {
write!(f, "{:#}", error)
write!(f, "{error:#}")
} else {
match (self.arg1.ok(), self.arg2.ok()) {
(Some(arg1), Some(arg2)) => {

View File

@@ -31,7 +31,7 @@ fdt = { workspace = true }
bytemuck = { workspace = true, features = ["derive", "min_const_generics"] }
num-traits = { workspace = true }
flate2 = { workspace = true, features = ["zlib-rs"] }
bzip2 = { workspace = true, features = ["libbz2-rs-sys"] }
bzip2 = { workspace = true }
lz4 = { workspace = true }
xz2 = { workspace = true }
zopfli = { workspace = true, features = ["gzip"] }

View File

@@ -48,6 +48,43 @@ static bool check_env(const char *name) {
return val != nullptr && val == "true"sv;
}
FileFormat check_fmt(const void *buf, size_t len) {
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
return FileFormat::CHROMEOS;
} else if (CHECKED_MATCH(BOOT_MAGIC)) {
return FileFormat::AOSP;
} else if (CHECKED_MATCH(VENDOR_BOOT_MAGIC)) {
return FileFormat::AOSP_VENDOR;
} else if (CHECKED_MATCH(GZIP1_MAGIC) || CHECKED_MATCH(GZIP2_MAGIC)) {
return FileFormat::GZIP;
} else if (CHECKED_MATCH(LZOP_MAGIC)) {
return FileFormat::LZOP;
} else if (CHECKED_MATCH(XZ_MAGIC)) {
return FileFormat::XZ;
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
return FileFormat::LZMA;
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
return FileFormat::BZIP2;
} else if (CHECKED_MATCH(LZ41_MAGIC) || CHECKED_MATCH(LZ42_MAGIC)) {
return FileFormat::LZ4;
} else if (CHECKED_MATCH(LZ4_LEG_MAGIC)) {
return FileFormat::LZ4_LEGACY;
} else if (CHECKED_MATCH(MTK_MAGIC)) {
return FileFormat::MTK;
} else if (CHECKED_MATCH(DTB_MAGIC)) {
return FileFormat::DTB;
} else if (CHECKED_MATCH(DHTB_MAGIC)) {
return FileFormat::DHTB;
} else if (CHECKED_MATCH(TEGRABLOB_MAGIC)) {
return FileFormat::BLOB;
} else if (len >= 0x28 && memcmp(&((char *)buf)[0x24], ZIMAGE_MAGIC, 4) == 0) {
return FileFormat::ZIMAGE;
} else {
return FileFormat::UNKNOWN;
}
}
void dyn_img_hdr::print() const {
uint32_t ver = header_version();
fprintf(stderr, "%-*s [%u]\n", PADDING, "HEADER_VER", ver);
@@ -472,7 +509,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
fprintf(stderr, "! Could not find zImage piggy, keeping raw kernel\n");
}
}
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name[k_fmt]);
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name(k_fmt));
}
if (auto size = hdr->ramdisk_size()) {
if (hdr->vendor_ramdisk_table_size()) {
@@ -493,7 +530,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
fprintf(stderr,
"%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK",
it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type),
it.ramdisk_size, fmt2name[fmt]);
it.ramdisk_size, fmt2name(fmt));
}
} else {
r_fmt = check_fmt_lg(ramdisk, size);
@@ -507,12 +544,12 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
hdr->ramdisk_size() -= sizeof(mtk_hdr);
r_fmt = check_fmt_lg(ramdisk, hdr->ramdisk_size());
}
fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name[r_fmt]);
fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name(r_fmt));
}
}
if (auto size = hdr->extra_size()) {
e_fmt = check_fmt_lg(extra, size);
fprintf(stderr, "%-*s [%s]\n", PADDING, "EXTRA_FMT", fmt2name[e_fmt]);
fprintf(stderr, "%-*s [%s]\n", PADDING, "EXTRA_FMT", fmt2name(e_fmt));
}
if (tail.sz()) {
@@ -523,10 +560,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
} else if (tail.sz() >= 16 && BUFFER_MATCH(tail.buf(), LG_BUMP_MAGIC)) {
fprintf(stderr, "LG_BUMP_IMAGE\n");
flags[LG_BUMP_FLAG] = true;
}
// Check if the image is signed
if (verify()) {
} else if (verify()) {
fprintf(stderr, "AVB1_SIGNED\n");
flags[AVB1_SIGNED_FLAG] = true;
}
@@ -534,13 +568,13 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
// Find AVB footer
const void *footer = tail.buf() + tail.sz() - sizeof(AvbFooter);
if (BUFFER_MATCH(footer, AVB_FOOTER_MAGIC)) {
avb_footer = reinterpret_cast<const AvbFooter*>(footer);
avb_footer = static_cast<const AvbFooter*>(footer);
// Double check if meta header exists
const void *meta = base_addr + __builtin_bswap64(avb_footer->vbmeta_offset);
if (BUFFER_MATCH(meta, AVB_MAGIC)) {
fprintf(stderr, "VBMETA\n");
flags[AVB_FLAG] = true;
vbmeta = reinterpret_cast<const AvbVBMetaImageHeader*>(meta);
vbmeta = static_cast<const AvbVBMetaImageHeader*>(meta);
}
}
}
@@ -549,16 +583,12 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
return true;
}
bool boot_img::verify(const char *cert) const {
return rust::verify_boot_image(*this, cert);
}
int split_image_dtb(const char *filename, bool skip_decomp) {
mmap_data img(filename);
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp) {
mmap_data img(filename.data());
if (size_t off = find_dtb_offset(img.buf(), img.sz()); off > 0) {
FileFormat fmt = check_fmt_lg(img.buf(), img.sz());
if (!skip_decomp && COMPRESSED(fmt)) {
if (!skip_decomp && fmt_compressed(fmt)) {
int fd = creat(KERNEL_FILE, 0644);
decompress(fmt, fd, img.buf(), off);
close(fd);
@@ -568,19 +598,19 @@ int split_image_dtb(const char *filename, bool skip_decomp) {
dump(img.buf() + off, img.sz() - off, KER_DTB_FILE);
return 0;
} else {
fprintf(stderr, "Cannot find DTB in %s\n", filename);
fprintf(stderr, "Cannot find DTB in %s\n", filename.data());
return 1;
}
}
int unpack(const char *image, bool skip_decomp, bool hdr) {
const boot_img boot(image);
int unpack(rust::Utf8CStr image, bool skip_decomp, bool hdr) {
const boot_img boot(image.data());
if (hdr)
boot.hdr->dump_hdr_file();
// Dump kernel
if (!skip_decomp && COMPRESSED(boot.k_fmt)) {
if (!skip_decomp && fmt_compressed(boot.k_fmt)) {
if (boot.hdr->kernel_size() != 0) {
int fd = creat(KERNEL_FILE, 0644);
decompress(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size());
@@ -611,13 +641,13 @@ int unpack(const char *image, bool skip_decomp, bool hdr) {
}
owned_fd fd = xopenat(dirfd, file_name, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644);
FileFormat fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
if (!skip_decomp && COMPRESSED(fmt)) {
if (!skip_decomp && fmt_compressed(fmt)) {
decompress(fmt, fd, boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
} else {
xwrite(fd, boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
}
}
} else if (!skip_decomp && COMPRESSED(boot.r_fmt)) {
} else if (!skip_decomp && fmt_compressed(boot.r_fmt)) {
if (boot.hdr->ramdisk_size() != 0) {
int fd = creat(RAMDISK_FILE, 0644);
decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size());
@@ -631,7 +661,7 @@ int unpack(const char *image, bool skip_decomp, bool hdr) {
dump(boot.second, boot.hdr->second_size(), SECOND_FILE);
// Dump extra
if (!skip_decomp && COMPRESSED(boot.e_fmt)) {
if (!skip_decomp && fmt_compressed(boot.e_fmt)) {
if (boot.hdr->extra_size() != 0) {
int fd = creat(EXTRA_FILE, 0644);
decompress(boot.e_fmt, fd, boot.extra, boot.hdr->extra_size());
@@ -658,9 +688,9 @@ write_zero(fd, align_padding(lseek(fd, 0, SEEK_CUR) - off.header, page_size))
#define file_align() file_align_with(boot.hdr->page_size())
void repack(const char *src_img, const char *out_img, bool skip_comp) {
const boot_img boot(src_img);
fprintf(stderr, "Repack to boot image: [%s]\n", out_img);
void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
const boot_img boot(src_img.data());
fprintf(stderr, "Repack to boot image: [%s]\n", out_img.data());
struct {
uint32_t header;
@@ -689,7 +719,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
***************/
// Create new image
int fd = creat(out_img, 0644);
int fd = creat(out_img.data(), 0644);
if (boot.flags[DHTB_FLAG]) {
// Skip DHTB header
@@ -718,7 +748,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
}
if (access(KERNEL_FILE, R_OK) == 0) {
mmap_data m(KERNEL_FILE);
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(boot.k_fmt)) {
if (!skip_comp && !fmt_compressed_any(check_fmt(m.buf(), m.sz())) && fmt_compressed(boot.k_fmt)) {
// Always use zopfli for zImage compression
auto fmt = (boot.flags[ZIMAGE_KERNEL] && boot.k_fmt == FileFormat::GZIP) ? FileFormat::ZOPFLI : boot.k_fmt;
hdr->kernel_size() = compress_len(fmt, m, fd);
@@ -786,7 +816,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
mmap_data m(dirfd, file_name);
FileFormat fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
it.ramdisk_offset = ramdisk_offset;
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(fmt)) {
if (!skip_comp && !fmt_compressed_any(check_fmt(m.buf(), m.sz())) && fmt_compressed(fmt)) {
it.ramdisk_size = compress_len(fmt, m, fd);
} else {
it.ramdisk_size = xwrite(fd, m.buf(), m.sz());
@@ -803,10 +833,10 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
// A v4 boot image ramdisk will have to be merged with other vendor ramdisks,
// and they have to use the exact same compression method. v4 GKIs are required to
// use lz4 (legacy), so hardcode the format here.
fprintf(stderr, "RAMDISK_FMT: [%s] -> [%s]\n", fmt2name[r_fmt], fmt2name[FileFormat::LZ4_LEGACY]);
fprintf(stderr, "RAMDISK_FMT: [%s] -> [%s]\n", fmt2name(r_fmt), fmt2name(FileFormat::LZ4_LEGACY));
r_fmt = FileFormat::LZ4_LEGACY;
}
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(r_fmt)) {
if (!skip_comp && !fmt_compressed_any(check_fmt(m.buf(), m.sz())) && fmt_compressed(r_fmt)) {
hdr->ramdisk_size() = compress_len(r_fmt, m, fd);
} else {
hdr->ramdisk_size() = xwrite(fd, m.buf(), m.sz());
@@ -825,7 +855,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
off.extra = lseek(fd, 0, SEEK_CUR);
if (access(EXTRA_FILE, R_OK) == 0) {
mmap_data m(EXTRA_FILE);
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(boot.e_fmt)) {
if (!skip_comp && !fmt_compressed_any(check_fmt(m.buf(), m.sz())) && fmt_compressed(boot.e_fmt)) {
hdr->extra_size() = compress_len(boot.e_fmt, m, fd);
} else {
hdr->extra_size() = xwrite(fd, m.buf(), m.sz());
@@ -901,7 +931,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
******************/
// Map output image as rw
mmap_data out(out_img, true);
mmap_data out(out_img.data(), true);
// MTK headers
if (boot.flags[MTK_KERNEL]) {
@@ -989,7 +1019,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
// Sign the image after we finish patching the boot image
if (boot.flags[AVB1_SIGNED_FLAG]) {
byte_view payload(out.buf() + off.header, off.total - off.header);
auto sig = rust::sign_boot_image(payload, "/boot", nullptr, nullptr);
auto sig = sign_payload(payload);
if (!sig.empty()) {
lseek(fd, off.total, SEEK_SET);
xwrite(fd, sig.data(), sig.size());
@@ -999,33 +1029,15 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
close(fd);
}
int verify(const char *image, const char *cert) {
const boot_img boot(image);
if (cert == nullptr) {
// Boot image parsing already checks if the image is signed
return boot.flags[AVB1_SIGNED_FLAG] ? 0 : 1;
} else {
// Provide a custom certificate and re-verify
return boot.verify(cert) ? 0 : 1;
}
}
int sign(const char *image, const char *name, const char *cert, const char *key) {
const boot_img boot(image);
auto sig = rust::sign_boot_image(boot.payload, name, cert, key);
if (sig.empty())
return 1;
auto eof = boot.tail.buf() - boot.map.buf();
int fd = xopen(image, O_WRONLY | O_CLOEXEC);
if (lseek(fd, eof, SEEK_SET) != eof || xwrite(fd, sig.data(), sig.size()) != sig.size()) {
close(fd);
return 1;
}
if (auto off = lseek(fd, 0, SEEK_CUR); off < boot.map.sz()) {
// Wipe out rest of tail
write_zero(fd, boot.map.sz() - off);
}
close(fd);
return 0;
void cleanup() {
unlink(HEADER_FILE);
unlink(KERNEL_FILE);
unlink(RAMDISK_FILE);
unlink(SECOND_FILE);
unlink(KER_DTB_FILE);
unlink(EXTRA_FILE);
unlink(RECV_DTBO_FILE);
unlink(DTB_FILE);
unlink(BOOTCONFIG_FILE);
rm_rf(VND_RAMDISK_DIR);
}

View File

@@ -5,8 +5,6 @@
#include <bitset>
#include <cxx.h>
#include "format.hpp"
/******************
* Special Headers
*****************/
@@ -676,7 +674,12 @@ struct boot_img {
std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, FileFormat type);
// Rust FFI
static std::unique_ptr<boot_img> create(rust::Utf8CStr name) { return std::make_unique<boot_img>(name.c_str()); }
rust::Slice<const uint8_t> get_payload() const { return payload; }
rust::Slice<const uint8_t> get_tail() const { return tail; }
bool verify(const char *cert = nullptr) const;
bool is_signed() const { return flags[AVB1_SIGNED_FLAG]; }
uint64_t tail_off() const { return tail.buf() - map.buf(); }
// Implemented in Rust
bool verify() const noexcept;
};

440
native/src/boot/cli.rs Normal file
View File

@@ -0,0 +1,440 @@
use crate::compress::{compress, decompress};
use crate::cpio::{cpio_commands, print_cpio_usage};
use crate::dtb::{DtbAction, dtb_commands, print_dtb_usage};
use crate::ffi::{BootImage, FileFormat, cleanup, repack, split_image_dtb, unpack};
use crate::patch::hexpatch;
use crate::payload::extract_boot_from_payload;
use crate::sign::{sha1_hash, sign_boot_image};
use argh::FromArgs;
use base::{
CmdArgs, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, WriteExt,
cmdline_logging, cstr, libc, libc::umask, log_err,
};
use std::ffi::c_char;
use std::io::{Seek, SeekFrom, Write};
use std::str::FromStr;
#[derive(FromArgs)]
struct Cli {
#[argh(subcommand)]
action: Action,
}
#[derive(FromArgs)]
#[argh(subcommand)]
enum Action {
Unpack(Unpack),
Repack(Repack),
Verify(Verify),
Sign(Sign),
Extract(Extract),
HexPatch(HexPatch),
Cpio(Cpio),
Dtb(Dtb),
Split(Split),
Sha1(Sha1),
Cleanup(Cleanup),
Compress(Compress),
Decompress(Decompress),
}
#[derive(FromArgs)]
#[argh(subcommand, name = "unpack")]
struct Unpack {
#[argh(switch, short = 'n')]
no_decompress: bool,
#[argh(switch, short = 'h')]
dump_header: bool,
#[argh(positional)]
img: String,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "repack")]
struct Repack {
#[argh(switch, short = 'n')]
no_compress: bool,
#[argh(positional)]
img: String,
#[argh(positional, default = r#""new-boot.img".to_string()"#)]
out: String,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "verify")]
struct Verify {
#[argh(positional)]
img: String,
#[argh(positional)]
cert: Option<String>,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "sign")]
struct Sign {
#[argh(positional)]
img: String,
#[argh(positional)]
args: Vec<String>,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "extract")]
struct Extract {
#[argh(positional)]
payload: String,
#[argh(positional)]
args: Vec<String>,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "hexpatch")]
struct HexPatch {
#[argh(positional)]
file: String,
#[argh(positional)]
src: String,
#[argh(positional)]
dest: String,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "cpio")]
struct Cpio {
#[argh(positional)]
file: String,
#[argh(positional)]
cmds: Vec<String>,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "dtb")]
struct Dtb {
#[argh(positional)]
file: String,
#[argh(subcommand)]
action: DtbAction,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "split")]
struct Split {
#[argh(switch, short = 'n')]
no_decompress: bool,
#[argh(positional)]
file: String,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "sha1")]
struct Sha1 {
#[argh(positional)]
file: String,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "cleanup")]
struct Cleanup {}
#[derive(FromArgs)]
#[argh(subcommand, name = "compress")]
struct Compress {
#[argh(option, short = 'f', default = r#""gzip".to_string()"#)]
format: String,
#[argh(positional)]
file: String,
#[argh(positional)]
out: Option<String>,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "decompress")]
struct Decompress {
#[argh(positional)]
file: String,
#[argh(positional)]
out: Option<String>,
}
fn print_usage(cmd: &str) {
eprintln!(
r#"MagiskBoot - Boot Image Modification Tool
Usage: {0} <action> [args...]
Supported actions:
unpack [-n] [-h] <bootimg>
Unpack <bootimg> to its individual components, each component to
a file with its corresponding file name in the current directory.
Supported components: kernel, kernel_dtb, ramdisk.cpio, second,
dtb, extra, and recovery_dtbo.
By default, each component will be decompressed on-the-fly.
If '-n' is provided, all decompression operations will be skipped;
each component will remain untouched, dumped in its original format.
If '-h' is provided, the boot image header information will be
dumped to the file 'header', which can be used to modify header
configurations during repacking.
Return values:
0:valid 1:error 2:chromeos
repack [-n] <origbootimg> [outbootimg]
Repack boot image components using files from the current directory
to [outbootimg], or 'new-boot.img' if not specified. Current directory
should only contain required files for [outbootimg], or incorrect
[outbootimg] may be produced.
<origbootimg> is the original boot image used to unpack the components.
By default, each component will be automatically compressed using its
corresponding format detected in <origbootimg>. If a component file
in the current directory is already compressed, then no addition
compression will be performed for that specific component.
If '-n' is provided, all compression operations will be skipped.
If env variable PATCHVBMETAFLAG is set to true, all disable flags in
the boot image's vbmeta header will be set.
verify <bootimg> [x509.pem]
Check whether the boot image is signed with AVB 1.0 signature.
Optionally provide a certificate to verify whether the image is
signed by the public key certificate.
Return value:
0:valid 1:error
sign <bootimg> [name] [x509.pem pk8]
Sign <bootimg> with AVB 1.0 signature.
Optionally provide the name of the image (default: '/boot').
Optionally provide the certificate/private key pair for signing.
If the certificate/private key pair is not provided, the AOSP
verity key bundled in the executable will be used.
extract <payload.bin> [partition] [outfile]
Extract [partition] from <payload.bin> to [outfile].
If [outfile] is not specified, then output to '[partition].img'.
If [partition] is not specified, then attempt to extract either
'init_boot' or 'boot'. Which partition was chosen can be determined
by whichever 'init_boot.img' or 'boot.img' exists.
<payload.bin> can be '-' to be STDIN.
hexpatch <file> <hexpattern1> <hexpattern2>
Search <hexpattern1> in <file>, and replace it with <hexpattern2>
cpio <incpio> [commands...]
Do cpio commands to <incpio> (modifications are done in-place).
Each command is a single argument; add quotes for each command.
See "cpio --help" for supported commands.
dtb <file> <action> [args...]
Do dtb related actions to <file>.
See "dtb --help" for supported actions.
split [-n] <file>
Split image.*-dtb into kernel + kernel_dtb.
If '-n' is provided, decompression operations will be skipped;
the kernel will remain untouched, split in its original format.
sha1 <file>
Print the SHA1 checksum for <file>
cleanup
Cleanup the current working directory
compress[=format] <infile> [outfile]
Compress <infile> with [format] to [outfile].
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
If [format] is not specified, then gzip will be used.
If [outfile] is not specified, then <infile> will be replaced
with another file suffixed with a matching file extension.
Supported formats:
{1}
decompress <infile> [outfile]
Detect format and decompress <infile> to [outfile].
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
If [outfile] is not specified, then <infile> will be replaced
with another file removing its archive format file extension.
Supported formats:
{1}
"#,
cmd,
FileFormat::formats()
);
}
fn verify_cmd(image: &Utf8CStr, cert: Option<&Utf8CStr>) -> bool {
let image = BootImage::new(image);
match cert {
None => {
// Boot image parsing already checks if the image is signed
image.is_signed()
}
Some(_) => {
// Provide a custom certificate and re-verify
image.verify(cert).is_ok()
}
}
}
fn sign_cmd(
image: &Utf8CStr,
name: Option<&Utf8CStr>,
cert: Option<&Utf8CStr>,
key: Option<&Utf8CStr>,
) -> LoggedResult<()> {
let img = BootImage::new(image);
let name = name.unwrap_or(cstr!("/boot"));
let sig = sign_boot_image(img.payload(), name, cert, key)?;
let tail_off = img.tail_off();
drop(img);
let mut fd = image.open(libc::O_WRONLY | libc::O_CLOEXEC)?;
fd.seek(SeekFrom::Start(tail_off))?;
fd.write_all(&sig)?;
let current = fd.stream_position()?;
let eof = fd.seek(SeekFrom::End(0))?;
if eof > current {
// Zero out rest of the file
fd.seek(SeekFrom::Start(current))?;
fd.write_zeros((eof - current) as usize)?;
}
Ok(())
}
fn boot_main(cmds: CmdArgs) -> LoggedResult<i32> {
let mut cmds = cmds.0;
if cmds.len() < 2 {
print_usage(cmds.first().unwrap_or(&"magiskboot"));
return log_err!();
}
if cmds[1].starts_with("--") {
cmds[1] = &cmds[1][2..];
}
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 Ok(unpack(
Utf8CStr::from_string(img),
no_decompress,
dump_header,
));
}
Action::Repack(Repack {
no_compress,
mut img,
mut out,
}) => {
repack(
Utf8CStr::from_string(&mut img),
Utf8CStr::from_string(&mut out),
no_compress,
);
}
Action::Verify(Verify { mut img, mut cert }) => {
if !verify_cmd(
Utf8CStr::from_string(&mut img),
cert.as_mut().map(Utf8CStr::from_string),
) {
return log_err!();
}
}
Action::Sign(Sign { mut img, mut args }) => {
let mut iter = args.iter_mut();
sign_cmd(
Utf8CStr::from_string(&mut img),
iter.next().map(Utf8CStr::from_string),
iter.next().map(Utf8CStr::from_string),
iter.next().map(Utf8CStr::from_string),
)?;
}
Action::Extract(Extract { payload, args }) => {
if args.len() > 2 {
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 {
mut file,
mut src,
mut dest,
}) => {
if !hexpatch(
&mut file,
Utf8CStr::from_string(&mut src),
Utf8CStr::from_string(&mut dest),
) {
log_err!("Failed to patch")?;
}
}
Action::Cpio(Cpio { mut file, mut cmds }) => {
cpio_commands(Utf8CStr::from_string(&mut file), &mut cmds)
.log_with_msg(|w| w.write_str("Failed to process cpio"))?;
}
Action::Dtb(Dtb { mut file, action }) => {
return dtb_commands(Utf8CStr::from_string(&mut 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,
mut file,
}) => {
return Ok(split_image_dtb(
Utf8CStr::from_string(&mut file),
no_decompress,
));
}
Action::Sha1(Sha1 { mut file }) => {
let file = MappedFile::open(Utf8CStr::from_string(&mut 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 { mut file, mut out }) => {
decompress(&mut 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(),
)?;
}
}
Ok(0)
}
#[unsafe(no_mangle)]
pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *const c_char) -> i32 {
cmdline_logging();
unsafe { umask(0) };
let cmds = CmdArgs::new(argc, argv);
boot_main(cmds).unwrap_or(1)
}

View File

@@ -1,5 +1,6 @@
use crate::ffi::FileFormat;
use base::{Chunker, LoggedResult, WriteExt};
use crate::ffi::{FileFormat, check_fmt};
use base::libc::{O_RDONLY, O_TRUNC, O_WRONLY};
use base::{Chunker, LoggedResult, Utf8CStr, WriteExt, error, log_err};
use bytemuck::bytes_of_mut;
use bzip2::{Compression as BzCompression, write::BzDecoder, write::BzEncoder};
use flate2::{Compression as GzCompression, write::GzEncoder, write::MultiGzDecoder};
@@ -9,11 +10,11 @@ use lz4::{
};
use std::cell::Cell;
use std::fs::File;
use std::io::{BufWriter, Read, Write};
use std::io::{BufWriter, Read, Write, stdin, stdout};
use std::mem::ManuallyDrop;
use std::num::NonZeroU64;
use std::ops::DerefMut;
use std::os::fd::{FromRawFd, RawFd};
use std::os::fd::{AsFd, AsRawFd, FromRawFd, RawFd};
use xz2::{
stream::{Check as LzmaCheck, Filters as LzmaFilters, LzmaOptions, Stream as LzmaStream},
write::{XzDecoder, XzEncoder},
@@ -424,3 +425,110 @@ pub fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
decoder.finish()?;
};
}
pub(crate) fn decompress(infile: &mut String, outfile: Option<&mut String>) -> LoggedResult<()> {
let in_std = infile == "-";
let mut rm_in = false;
let mut buf = [0u8; 4096];
let raw_in = if in_std {
super let mut stdin = stdin();
let _ = stdin.read(&mut buf)?;
stdin.as_fd()
} else {
super let mut infile = Utf8CStr::from_string(infile).open(O_RDONLY)?;
let _ = infile.read(&mut buf)?;
infile.as_fd()
};
let format = check_fmt(&buf);
eprintln!("Detected format: {format}");
if !format.is_compressed() {
return log_err!("Input file is not a supported type!");
}
let raw_out = if let Some(outfile) = outfile {
if outfile == "-" {
super let stdout = stdout();
stdout.as_fd()
} else {
super let outfile = Utf8CStr::from_string(outfile).create(O_WRONLY | O_TRUNC, 0o644)?;
outfile.as_fd()
}
} else if in_std {
super let stdout = stdout();
stdout.as_fd()
} else {
// strip the extension
rm_in = true;
let mut outfile = if let Some((outfile, ext)) = infile.rsplit_once('.') {
if ext != format.ext() {
log_err!("Input file is not a supported type!")?;
}
outfile.to_owned()
} else {
infile.clone()
};
eprintln!("Decompressing to [{outfile}]");
super let outfile = Utf8CStr::from_string(&mut outfile).create(O_WRONLY | O_TRUNC, 0o644)?;
outfile.as_fd()
};
decompress_bytes_fd(format, &buf, raw_in.as_raw_fd(), raw_out.as_raw_fd());
if rm_in {
Utf8CStr::from_string(infile).remove()?;
}
Ok(())
}
pub(crate) fn compress(
method: FileFormat,
infile: &mut String,
outfile: Option<&mut String>,
) -> LoggedResult<()> {
if method == FileFormat::UNKNOWN {
error!("Unsupported compression format");
}
let in_std = infile == "-";
let mut rm_in = false;
let raw_in = if in_std {
super let stdin = stdin();
stdin.as_fd()
} else {
super let infile = Utf8CStr::from_string(infile).open(O_RDONLY)?;
infile.as_fd()
};
let raw_out = if let Some(outfile) = outfile {
if outfile == "-" {
super let stdout = stdout();
stdout.as_fd()
} else {
super let outfile = Utf8CStr::from_string(outfile).create(O_WRONLY | O_TRUNC, 0o644)?;
outfile.as_fd()
}
} else if in_std {
super let stdout = stdout();
stdout.as_fd()
} else {
let mut outfile = format!("{infile}.{}", method.ext());
eprintln!("Compressing to [{outfile}]");
rm_in = true;
super let outfile = Utf8CStr::from_string(&mut outfile).create(O_WRONLY | O_TRUNC, 0o644)?;
outfile.as_fd()
};
compress_fd(method, raw_in.as_raw_fd(), raw_out.as_raw_fd());
if rm_in {
Utf8CStr::from_string(infile).remove()?;
}
Ok(())
}

View File

@@ -17,11 +17,11 @@ use size::{Base, Size, Style};
use base::libc::{
O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT,
S_IFREG, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
c_char, dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t,
dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t,
};
use base::{
BytesExt, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt,
cstr, log_err, map_args,
BytesExt, EarlyExitExt, LoggedResult, MappedFile, OptionExt, ResultExt, Utf8CStr, Utf8CStrBuf,
WriteExt, cstr, log_err,
};
use crate::check_env;
@@ -29,14 +29,6 @@ use crate::compress::{get_decoder, get_encoder};
use crate::ffi::FileFormat;
use crate::patch::{patch_encryption, patch_verity};
#[derive(FromArgs)]
struct CpioCli {
#[argh(positional)]
file: String,
#[argh(positional)]
commands: Vec<String>,
}
#[derive(FromArgs)]
struct CpioCommand {
#[argh(subcommand)]
@@ -151,7 +143,7 @@ struct List {
recursive: bool,
}
fn print_cpio_usage() {
pub(crate) fn print_cpio_usage() {
eprintln!(
r#"Usage: magiskboot cpio <incpio> [commands...]
@@ -235,7 +227,7 @@ impl Cpio {
let hdr_sz = size_of::<CpioHeader>();
let hdr = from_bytes::<CpioHeader>(&data[pos..(pos + hdr_sz)]);
if &hdr.magic != b"070701" {
return Err(log_err!("invalid cpio magic"));
return log_err!("invalid cpio magic");
}
pos += hdr_sz;
let name_sz = x8u(&hdr.namesize)? as usize;
@@ -269,13 +261,13 @@ impl Cpio {
}
fn load_from_file(path: &Utf8CStr) -> LoggedResult<Self> {
eprintln!("Loading cpio: [{}]", path);
eprintln!("Loading cpio: [{path}]");
let file = MappedFile::open(path)?;
Self::load_from_data(file.as_ref())
}
fn dump(&self, path: &str) -> LoggedResult<()> {
eprintln!("Dumping cpio: [{}]", path);
eprintln!("Dumping cpio: [{path}]");
let mut file = File::create(path)?;
let mut pos = 0usize;
let mut inode = 300000i64;
@@ -320,13 +312,13 @@ impl Cpio {
fn rm(&mut self, path: &str, recursive: bool) {
let path = norm_path(path);
if self.entries.remove(&path).is_some() {
eprintln!("Removed entry [{}]", path);
eprintln!("Removed entry [{path}]");
}
if recursive {
let path = path + "/";
self.entries.retain(|k, _| {
if k.starts_with(&path) {
eprintln!("Removed entry [{}]", k);
eprintln!("Removed entry [{k}]");
false
} else {
true
@@ -339,8 +331,8 @@ impl Cpio {
let entry = self
.entries
.get(path)
.ok_or_else(|| log_err!("No such file"))?;
eprintln!("Extracting entry [{}] to [{}]", path, out);
.ok_or_log_msg(|w| w.write_str("No such file"))?;
eprintln!("Extracting entry [{path}] to [{out}]");
let out = Utf8CStr::from_string(out);
@@ -370,7 +362,7 @@ impl Cpio {
unsafe { mknod(out.as_ptr().cast(), entry.mode, dev) };
}
_ => {
return Err(log_err!("unknown entry type"));
return log_err!("unknown entry type");
}
}
Ok(())
@@ -397,7 +389,7 @@ impl Cpio {
fn add(&mut self, mode: mode_t, path: &str, file: &mut String) -> LoggedResult<()> {
if path.ends_with('/') {
return Err(log_err!("path cannot end with / for add"));
return log_err!("path cannot end with / for add");
}
let file = Utf8CStr::from_string(file);
let attr = file.get_attr()?;
@@ -420,7 +412,7 @@ impl Cpio {
} else if attr.is_char_device() {
mode | S_IFCHR
} else {
return Err(log_err!("unsupported file type"));
return log_err!("unsupported file type");
}
};
@@ -435,7 +427,7 @@ impl Cpio {
data: content,
}),
);
eprintln!("Add file [{}] ({:04o})", path, mode);
eprintln!("Add file [{path}] ({mode:04o})");
Ok(())
}
@@ -451,7 +443,7 @@ impl Cpio {
data: vec![],
}),
);
eprintln!("Create directory [{}] ({:04o})", dir, mode);
eprintln!("Create directory [{dir}] ({mode:04o})");
}
fn ln(&mut self, src: &str, dst: &str) {
@@ -466,16 +458,16 @@ impl Cpio {
data: norm_path(src).as_bytes().to_vec(),
}),
);
eprintln!("Create symlink [{}] -> [{}]", dst, src);
eprintln!("Create symlink [{dst}] -> [{src}]");
}
fn mv(&mut self, from: &str, to: &str) -> LoggedResult<()> {
let entry = self
.entries
.remove(&norm_path(from))
.ok_or_else(|| log_err!("no such entry {}", from))?;
.ok_or_log_msg(|w| w.write_fmt(format_args!("No such entry {from}")))?;
self.entries.insert(norm_path(to), entry);
eprintln!("Move [{}] -> [{}]", from, to);
eprintln!("Move [{from}] -> [{to}]");
Ok(())
}
@@ -498,7 +490,7 @@ impl Cpio {
if !recursive && !p.is_empty() && p.matches('/').count() > 1 {
continue;
}
println!("{}\t{}", entry, name);
println!("{entry}\t{name}");
}
}
}
@@ -511,8 +503,7 @@ impl Cpio {
let keep_verity = check_env("KEEPVERITY");
let keep_force_encrypt = check_env("KEEPFORCEENCRYPT");
eprintln!(
"Patch with flag KEEPVERITY=[{}] KEEPFORCEENCRYPT=[{}]",
keep_verity, keep_force_encrypt
"Patch with flag KEEPVERITY=[{keep_verity}] KEEPFORCEENCRYPT=[{keep_force_encrypt}]"
);
self.entries.retain(|name, entry| {
let fstab = (!keep_verity || !keep_force_encrypt)
@@ -523,7 +514,7 @@ impl Cpio {
&& name.starts_with("fstab");
if !keep_verity {
if fstab {
eprintln!("Found fstab file [{}]", name);
eprintln!("Found fstab file [{name}]");
let len = patch_verity(entry.data.as_mut_slice());
if len != entry.data.len() {
entry.data.resize(len, 0);
@@ -581,7 +572,7 @@ impl Cpio {
} else {
&name[8..]
};
eprintln!("Restore [{}] -> [{}]", name, new_name);
eprintln!("Restore [{name}] -> [{new_name}]");
backups.insert(new_name.to_string(), entry);
}
});
@@ -658,16 +649,16 @@ impl Cpio {
match action {
Action::Backup(name, mut entry) => {
let backup = if !skip_compress && entry.compress() {
format!(".backup/{}.xz", name)
format!(".backup/{name}.xz")
} else {
format!(".backup/{}", name)
format!(".backup/{name}")
};
eprintln!("Backup [{}] -> [{}]", name, backup);
eprintln!("Backup [{name}] -> [{backup}]");
backups.insert(backup, entry);
}
Action::Record(name) => {
eprintln!("Record new entry: [{}] -> [.backup/.rmlist]", name);
rm_list.push_str(&format!("{}\0", name));
eprintln!("Record new entry: [{name}] -> [.backup/.rmlist]");
rm_list.push_str(&format!("{name}\0"));
}
Action::Noop => {}
}
@@ -762,74 +753,61 @@ impl Display for CpioEntry {
}
}
pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool {
let res: LoggedResult<()> = try {
if argc < 1 {
Err(log_err!("No arguments"))?;
}
let cmds = map_args(argc, argv)?;
let mut cli =
CpioCli::from_args(&["magiskboot", "cpio"], &cmds).on_early_exit(print_cpio_usage);
let file = Utf8CStr::from_string(&mut cli.file);
let mut cpio = if file.exists() {
Cpio::load_from_file(file)?
} else {
Cpio::new()
};
for cmd in cli.commands {
if cmd.starts_with('#') {
continue;
}
let mut cli = CpioCommand::from_args(
&["magiskboot", "cpio", file],
cmd.split(' ')
.filter(|x| !x.is_empty())
.collect::<Vec<_>>()
.as_slice(),
)
.on_early_exit(print_cpio_usage);
match &mut cli.action {
CpioAction::Test(_) => exit(cpio.test()),
CpioAction::Restore(_) => cpio.restore()?,
CpioAction::Patch(_) => cpio.patch(),
CpioAction::Exists(Exists { path }) => {
if cpio.exists(path) {
exit(0);
} else {
exit(1);
}
}
CpioAction::Backup(Backup {
origin,
skip_compress,
}) => cpio.backup(origin, *skip_compress)?,
CpioAction::Remove(Remove { path, recursive }) => cpio.rm(path, *recursive),
CpioAction::Move(Move { from, to }) => cpio.mv(from, to)?,
CpioAction::MakeDir(MakeDir { mode, dir }) => cpio.mkdir(*mode, dir),
CpioAction::Link(Link { src, dst }) => cpio.ln(src, dst),
CpioAction::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?,
CpioAction::Extract(Extract { paths }) => {
if !paths.is_empty() && paths.len() != 2 {
Err(log_err!("invalid arguments"))?;
}
let mut it = paths.iter_mut();
cpio.extract(it.next(), it.next())?;
}
CpioAction::List(List { path, recursive }) => {
cpio.ls(path.as_str(), *recursive);
exit(0);
}
};
}
cpio.dump(file)?;
pub(crate) fn cpio_commands(file: &Utf8CStr, cmds: &mut Vec<String>) -> LoggedResult<()> {
let mut cpio = if file.exists() {
Cpio::load_from_file(file)?
} else {
Cpio::new()
};
res.log_with_msg(|w| w.write_str("Failed to process cpio"))
.is_ok()
for cmd in cmds {
if cmd.starts_with('#') {
continue;
}
let mut cmd = CpioCommand::from_args(
&["magiskboot", "cpio", file],
cmd.split(' ')
.filter(|x| !x.is_empty())
.collect::<Vec<_>>()
.as_slice(),
)
.on_early_exit(print_cpio_usage);
match &mut cmd.action {
CpioAction::Test(_) => exit(cpio.test()),
CpioAction::Restore(_) => cpio.restore()?,
CpioAction::Patch(_) => cpio.patch(),
CpioAction::Exists(Exists { path }) => {
return if cpio.exists(path) {
Ok(())
} else {
log_err!()
};
}
CpioAction::Backup(Backup {
origin,
skip_compress,
}) => cpio.backup(origin, *skip_compress)?,
CpioAction::Remove(Remove { path, recursive }) => cpio.rm(path, *recursive),
CpioAction::Move(Move { from, to }) => cpio.mv(from, to)?,
CpioAction::MakeDir(MakeDir { mode, dir }) => cpio.mkdir(*mode, dir),
CpioAction::Link(Link { src, dst }) => cpio.ln(src, dst),
CpioAction::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?,
CpioAction::Extract(Extract { paths }) => {
if !paths.is_empty() && paths.len() != 2 {
log_err!("invalid arguments")?;
}
let mut it = paths.iter_mut();
cpio.extract(it.next(), it.next())?;
}
CpioAction::List(List { path, recursive }) => {
cpio.ls(path.as_str(), *recursive);
return Ok(());
}
};
}
cpio.dump(file)?;
Ok(())
}
fn x8u(x: &[u8; 8]) -> LoggedResult<u32> {
@@ -837,7 +815,9 @@ fn x8u(x: &[u8; 8]) -> LoggedResult<u32> {
let mut ret = 0u32;
let s = str::from_utf8(x).log_with_msg(|w| w.write_str("bad cpio header"))?;
for c in s.chars() {
ret = ret * 16 + c.to_digit(16).ok_or_else(|| log_err!("bad cpio header"))?;
ret = ret * 16
+ c.to_digit(16)
.ok_or_log_msg(|w| w.write_str("bad cpio header"))?;
}
Ok(ret)
}

View File

@@ -1,4 +1,4 @@
use std::{cell::UnsafeCell, process::exit};
use std::cell::UnsafeCell;
use argh::FromArgs;
use fdt::{
@@ -6,23 +6,13 @@ use fdt::{
node::{FdtNode, NodeProperty},
};
use base::{
EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, libc::c_char, log_err, map_args,
};
use base::{LoggedResult, MappedFile, Utf8CStr};
use crate::{check_env, patch::patch_verity};
#[derive(FromArgs)]
struct DtbCli {
#[argh(positional)]
file: String,
#[argh(subcommand)]
action: DtbAction,
}
#[derive(FromArgs)]
#[argh(subcommand)]
enum DtbAction {
pub(crate) enum DtbAction {
Print(Print),
Patch(Patch),
Test(Test),
@@ -30,20 +20,20 @@ enum DtbAction {
#[derive(FromArgs)]
#[argh(subcommand, name = "print")]
struct Print {
pub(crate) struct Print {
#[argh(switch, short = 'f')]
fstab: bool,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "patch")]
struct Patch {}
pub(crate) struct Patch {}
#[derive(FromArgs)]
#[argh(subcommand, name = "test")]
struct Test {}
pub(crate) struct Test {}
fn print_dtb_usage() {
pub(crate) fn print_dtb_usage() {
eprintln!(
r#"Usage: magiskboot dtb <file> <action> [args...]
Do dtb related actions to <file>.
@@ -131,9 +121,9 @@ fn print_node(node: &FdtNode) {
}
);
} else if size > MAX_PRINT_LEN {
println!("[{}]: <bytes>({})", name, size);
println!("[{name}]: <bytes>({size})");
} else {
println!("[{}]: {:02x?}", name, value);
println!("[{name}]: {value:02x?}");
}
}
@@ -154,7 +144,7 @@ fn for_each_fdt<F: FnMut(usize, Fdt) -> LoggedResult<()>>(
rw: bool,
mut f: F,
) -> LoggedResult<()> {
eprintln!("Loading dtbs from [{}]", file);
eprintln!("Loading dtbs from [{file}]");
let file = if rw {
MappedFile::open_rw(file)?
} else {
@@ -173,7 +163,7 @@ fn for_each_fdt<F: FnMut(usize, Fdt) -> LoggedResult<()>>(
}
let fdt = match Fdt::new(slice) {
Err(FdtError::BufferTooSmall) => {
eprintln!("dtb.{:04} is truncated", dtb_num);
eprintln!("dtb.{dtb_num:04} is truncated");
break;
}
Ok(fdt) => fdt,
@@ -198,11 +188,11 @@ fn dtb_print(file: &Utf8CStr, fstab: bool) -> LoggedResult<()> {
for_each_fdt(file, false, |n, fdt| {
if fstab {
if let Some(fstab) = find_fstab(&fdt) {
eprintln!("Found fstab in dtb.{:04}", n);
eprintln!("Found fstab in dtb.{n:04}");
print_node(&fstab);
}
} else if let Some(mut root) = fdt.find_node("/") {
eprintln!("Printing dtb.{:04}", n);
eprintln!("Printing dtb.{n:04}");
if root.name.is_empty() {
root.name = "/";
}
@@ -248,7 +238,7 @@ fn dtb_patch(file: &Utf8CStr) -> LoggedResult<bool> {
&mut *std::mem::transmute::<&[u8], &UnsafeCell<[u8]>>(w).get()
};
w[..=4].copy_from_slice(b"want");
eprintln!("Patch [skip_initramfs] -> [want_initramfs] in dtb.{:04}", n);
eprintln!("Patch [skip_initramfs] -> [want_initramfs] in dtb.{n:04}");
patched = true;
}
});
@@ -274,34 +264,13 @@ fn dtb_patch(file: &Utf8CStr) -> LoggedResult<bool> {
Ok(patched)
}
pub fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool {
let res: LoggedResult<()> = try {
if argc < 1 {
Err(log_err!("No arguments"))?;
pub(crate) fn dtb_commands(file: &Utf8CStr, action: &DtbAction) -> LoggedResult<bool> {
match action {
DtbAction::Print(Print { fstab }) => {
dtb_print(file, *fstab)?;
Ok(true)
}
let cmds = map_args(argc, argv)?;
let mut cli =
DtbCli::from_args(&["magiskboot", "dtb"], &cmds).on_early_exit(print_dtb_usage);
let file = Utf8CStr::from_string(&mut cli.file);
match cli.action {
DtbAction::Print(Print { fstab }) => {
dtb_print(file, fstab)?;
}
DtbAction::Test(_) => {
if !dtb_test(file)? {
exit(1);
}
}
DtbAction::Patch(_) => {
if !dtb_patch(file)? {
exit(1);
}
}
}
};
res.log_with_msg(|w| w.write_str("Failed to process dtb"))
.is_ok()
DtbAction::Test(_) => Ok(dtb_test(file)?),
DtbAction::Patch(_) => Ok(dtb_patch(file)?),
}
}

View File

@@ -1,111 +0,0 @@
#include "boot-rs.hpp"
#include "format.hpp"
Name2Fmt name2fmt;
Fmt2Name fmt2name;
Fmt2Ext fmt2ext;
#define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
FileFormat check_fmt(const void *buf, size_t len) {
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
return FileFormat::CHROMEOS;
} else if (CHECKED_MATCH(BOOT_MAGIC)) {
return FileFormat::AOSP;
} else if (CHECKED_MATCH(VENDOR_BOOT_MAGIC)) {
return FileFormat::AOSP_VENDOR;
} else if (CHECKED_MATCH(GZIP1_MAGIC) || CHECKED_MATCH(GZIP2_MAGIC)) {
return FileFormat::GZIP;
} else if (CHECKED_MATCH(LZOP_MAGIC)) {
return FileFormat::LZOP;
} else if (CHECKED_MATCH(XZ_MAGIC)) {
return FileFormat::XZ;
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
return FileFormat::LZMA;
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
return FileFormat::BZIP2;
} else if (CHECKED_MATCH(LZ41_MAGIC) || CHECKED_MATCH(LZ42_MAGIC)) {
return FileFormat::LZ4;
} else if (CHECKED_MATCH(LZ4_LEG_MAGIC)) {
return FileFormat::LZ4_LEGACY;
} else if (CHECKED_MATCH(MTK_MAGIC)) {
return FileFormat::MTK;
} else if (CHECKED_MATCH(DTB_MAGIC)) {
return FileFormat::DTB;
} else if (CHECKED_MATCH(DHTB_MAGIC)) {
return FileFormat::DHTB;
} else if (CHECKED_MATCH(TEGRABLOB_MAGIC)) {
return FileFormat::BLOB;
} else if (len >= 0x28 && memcmp(&((char *)buf)[0x24], ZIMAGE_MAGIC, 4) == 0) {
return FileFormat::ZIMAGE;
} else {
return FileFormat::UNKNOWN;
}
}
const char *Fmt2Name::operator[](FileFormat fmt) {
switch (fmt) {
case FileFormat::GZIP:
return "gzip";
case FileFormat::ZOPFLI:
return "zopfli";
case FileFormat::LZOP:
return "lzop";
case FileFormat::XZ:
return "xz";
case FileFormat::LZMA:
return "lzma";
case FileFormat::BZIP2:
return "bzip2";
case FileFormat::LZ4:
return "lz4";
case FileFormat::LZ4_LEGACY:
return "lz4_legacy";
case FileFormat::LZ4_LG:
return "lz4_lg";
case FileFormat::DTB:
return "dtb";
case FileFormat::ZIMAGE:
return "zimage";
default:
return "raw";
}
}
const char *Fmt2Ext::operator[](FileFormat fmt) {
switch (fmt) {
case FileFormat::GZIP:
case FileFormat::ZOPFLI:
return ".gz";
case FileFormat::LZOP:
return ".lzo";
case FileFormat::XZ:
return ".xz";
case FileFormat::LZMA:
return ".lzma";
case FileFormat::BZIP2:
return ".bz2";
case FileFormat::LZ4:
case FileFormat::LZ4_LEGACY:
case FileFormat::LZ4_LG:
return ".lz4";
default:
return "";
}
}
#define CHECK(s, f) else if (name == s) return f;
FileFormat Name2Fmt::operator[](std::string_view name) {
if (0) {}
CHECK("gzip", FileFormat::GZIP)
CHECK("zopfli", FileFormat::ZOPFLI)
CHECK("xz", FileFormat::XZ)
CHECK("lzma", FileFormat::LZMA)
CHECK("bzip2", FileFormat::BZIP2)
CHECK("lz4", FileFormat::LZ4)
CHECK("lz4_legacy", FileFormat::LZ4_LEGACY)
CHECK("lz4_lg", FileFormat::LZ4_LG)
else return FileFormat::UNKNOWN;
}

View File

@@ -1,67 +0,0 @@
#pragma once
#include <string_view>
enum class FileFormat : ::std::uint8_t;
#define COMPRESSED(fmt) ((+fmt) >= +FileFormat::GZIP && (+fmt) < +FileFormat::LZOP)
#define COMPRESSED_ANY(fmt) ((+fmt) >= +FileFormat::GZIP && (+fmt) <= +FileFormat::LZOP)
#define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0)
#define BUFFER_CONTAIN(buf, sz, s) (memmem(buf, sz, s, sizeof(s) - 1) != nullptr)
#define BOOT_MAGIC "ANDROID!"
#define VENDOR_BOOT_MAGIC "VNDRBOOT"
#define CHROMEOS_MAGIC "CHROMEOS"
#define GZIP1_MAGIC "\x1f\x8b"
#define GZIP2_MAGIC "\x1f\x9e"
#define LZOP_MAGIC "\x89""LZO"
#define XZ_MAGIC "\xfd""7zXZ"
#define BZIP_MAGIC "BZh"
#define LZ4_LEG_MAGIC "\x02\x21\x4c\x18"
#define LZ41_MAGIC "\x03\x21\x4c\x18"
#define LZ42_MAGIC "\x04\x22\x4d\x18"
#define MTK_MAGIC "\x88\x16\x88\x58"
#define DTB_MAGIC "\xd0\x0d\xfe\xed"
#define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79"
#define DHTB_MAGIC "\x44\x48\x54\x42\x01\x00\x00\x00"
#define SEANDROID_MAGIC "SEANDROIDENFORCE"
#define TEGRABLOB_MAGIC "-SIGNED-BY-SIGNBLOB-"
#define NOOKHD_RL_MAGIC "Red Loader"
#define NOOKHD_GL_MAGIC "Green Loader"
#define NOOKHD_GR_MAGIC "Green Recovery"
#define NOOKHD_EB_MAGIC "eMMC boot.img+secondloader"
#define NOOKHD_ER_MAGIC "eMMC recovery.img+secondloader"
#define NOOKHD_PRE_HEADER_SZ 1048576
#define ACCLAIM_MAGIC "BauwksBoot"
#define ACCLAIM_PRE_HEADER_SZ 262144
#define AMONET_MICROLOADER_MAGIC "microloader"
#define AMONET_MICROLOADER_SZ 1024
#define AVB_FOOTER_MAGIC "AVBf"
#define AVB_MAGIC "AVB0"
#define ZIMAGE_MAGIC "\x18\x28\x6f\x01"
class Fmt2Name {
public:
const char *operator[](FileFormat fmt);
};
class Fmt2Ext {
public:
const char *operator[](FileFormat fmt);
};
class Name2Fmt {
public:
FileFormat operator[](std::string_view name);
};
FileFormat check_fmt(const void *buf, size_t len);
static inline FileFormat check_fmt(rust::Slice<const uint8_t> bytes) {
return check_fmt(bytes.data(), bytes.size());
}
extern Name2Fmt name2fmt;
extern Fmt2Name fmt2name;
extern Fmt2Ext fmt2ext;

104
native/src/boot/format.rs Normal file
View File

@@ -0,0 +1,104 @@
use crate::ffi::FileFormat;
use base::{Utf8CStr, cstr, libc};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
impl FromStr for FileFormat {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"gzip" => Ok(Self::GZIP),
"zopfli" => Ok(Self::ZOPFLI),
"xz" => Ok(Self::XZ),
"lzma" => Ok(Self::LZMA),
"bzip2" => Ok(Self::BZIP2),
"lz4" => Ok(Self::LZ4),
"lz4_legacy" => Ok(Self::LZ4_LEGACY),
"lz4_lg" => Ok(Self::LZ4_LG),
_ => Err(()),
}
}
}
impl Display for FileFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_cstr())
}
}
impl FileFormat {
fn as_cstr(&self) -> &'static Utf8CStr {
match *self {
Self::GZIP => cstr!("gzip"),
Self::ZOPFLI => cstr!("zopfli"),
Self::LZOP => cstr!("lzop"),
Self::XZ => cstr!("xz"),
Self::LZMA => cstr!("lzma"),
Self::BZIP2 => cstr!("bzip2"),
Self::LZ4 => cstr!("lz4"),
Self::LZ4_LEGACY => cstr!("lz4_legacy"),
Self::LZ4_LG => cstr!("lz4_lg"),
Self::DTB => cstr!("dtb"),
Self::ZIMAGE => cstr!("zimage"),
_ => cstr!("raw"),
}
}
}
impl FileFormat {
pub fn ext(&self) -> &'static str {
match *self {
Self::GZIP | Self::ZOPFLI => ".gz",
Self::LZOP => ".lzo",
Self::XZ => ".xz",
Self::LZMA => ".lzma",
Self::BZIP2 => ".bz2",
Self::LZ4 | Self::LZ4_LEGACY | Self::LZ4_LG => ".lz4",
_ => "",
}
}
pub fn is_compressed(&self) -> bool {
matches!(
*self,
Self::GZIP
| Self::ZOPFLI
| Self::XZ
| Self::LZMA
| Self::BZIP2
| Self::LZ4
| Self::LZ4_LEGACY
| Self::LZ4_LG
)
}
pub fn formats() -> String {
[
Self::GZIP,
Self::ZOPFLI,
Self::XZ,
Self::LZMA,
Self::BZIP2,
Self::LZ4,
Self::LZ4_LEGACY,
Self::LZ4_LG,
]
.map(|f| f.to_string())
.join(" ")
}
}
// C++ FFI
pub fn fmt2name(fmt: FileFormat) -> *const libc::c_char {
fmt.as_cstr().as_ptr()
}
pub fn fmt_compressed(fmt: FileFormat) -> bool {
fmt.is_compressed()
}
pub fn fmt_compressed_any(fmt: FileFormat) -> bool {
fmt.is_compressed() || matches!(fmt, FileFormat::LZOP)
}

View File

@@ -2,19 +2,19 @@
#![feature(btree_extract_if)]
#![feature(iter_intersperse)]
#![feature(try_blocks)]
#![feature(super_let)]
pub use base;
use compress::{compress_bytes, compress_fd, decompress_bytes, decompress_bytes_fd};
use cpio::cpio_commands;
use dtb::dtb_commands;
use patch::hexpatch;
use payload::extract_boot_from_payload;
use sign::{SHA, get_sha, sha1_hash, sha256_hash, sign_boot_image, verify_boot_image};
use compress::{compress_bytes, decompress_bytes};
use format::{fmt_compressed, fmt_compressed_any, fmt2name};
use sign::{SHA, get_sha, sha256_hash, sign_payload_for_cxx};
use std::env;
mod cli;
mod compress;
mod cpio;
mod dtb;
mod format;
mod patch;
mod payload;
// Suppress warnings in generated code
@@ -58,16 +58,12 @@ pub mod ffi {
}
unsafe extern "C++" {
include!("format.hpp");
include!("magiskboot.hpp");
fn cleanup();
fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32;
fn repack(src_img: Utf8CStrRef, out_img: Utf8CStrRef, skip_comp: bool);
fn split_image_dtb(filename: Utf8CStrRef, skip_decomp: bool) -> i32;
fn check_fmt(buf: &[u8]) -> FileFormat;
include!("bootimg.hpp");
#[cxx_name = "boot_img"]
type BootImage;
#[cxx_name = "get_payload"]
fn payload(self: &BootImage) -> &[u8];
#[cxx_name = "get_tail"]
fn tail(self: &BootImage) -> &[u8];
}
extern "Rust" {
@@ -76,33 +72,38 @@ pub mod ffi {
fn update(self: &mut SHA, data: &[u8]);
fn finalize_into(self: &mut SHA, out: &mut [u8]);
fn output_size(self: &SHA) -> usize;
fn sha1_hash(data: &[u8], out: &mut [u8]);
fn sha256_hash(data: &[u8], out: &mut [u8]);
fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool;
fn compress_fd(format: FileFormat, in_fd: i32, out_fd: i32);
fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
fn decompress_bytes_fd(format: FileFormat, in_bytes: &[u8], in_fd: i32, out_fd: i32);
fn fmt2name(fmt: FileFormat) -> *const c_char;
fn fmt_compressed(fmt: FileFormat) -> bool;
fn fmt_compressed_any(fmt: FileFormat) -> bool;
#[cxx_name = "sign_payload"]
fn sign_payload_for_cxx(payload: &[u8]) -> Vec<u8>;
}
#[namespace = "rust"]
#[allow(unused_unsafe)]
// BootImage FFI
unsafe extern "C++" {
include!("bootimg.hpp");
#[cxx_name = "boot_img"]
type BootImage;
#[cxx_name = "get_payload"]
fn payload(self: &BootImage) -> &[u8];
#[cxx_name = "get_tail"]
fn tail(self: &BootImage) -> &[u8];
fn is_signed(self: &BootImage) -> bool;
fn tail_off(self: &BootImage) -> u64;
#[Self = BootImage]
#[cxx_name = "create"]
fn new(img: Utf8CStrRef) -> UniquePtr<BootImage>;
}
extern "Rust" {
fn extract_boot_from_payload(
partition: Utf8CStrRef,
in_path: Utf8CStrRef,
out_path: Utf8CStrRef,
) -> bool;
unsafe fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool;
unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool;
unsafe fn sign_boot_image(
payload: &[u8],
name: *const c_char,
cert: *const c_char,
key: *const c_char,
) -> Vec<u8>;
unsafe fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool;
#[cxx_name = "verify"]
fn verify_for_cxx(self: &BootImage) -> bool;
}
}

View File

@@ -12,8 +12,49 @@
#define BOOTCONFIG_FILE "bootconfig"
#define NEW_BOOT "new-boot.img"
int unpack(const char *image, bool skip_decomp = false, bool hdr = false);
void repack(const char *src_img, const char *out_img, bool skip_comp = false);
int verify(const char *image, const char *cert);
int sign(const char *image, const char *name, const char *cert, const char *key);
int split_image_dtb(const char *filename, bool skip_decomp = false);
#define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0)
#define BUFFER_CONTAIN(buf, sz, s) (memmem(buf, sz, s, sizeof(s) - 1) != nullptr)
#define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
#define BOOT_MAGIC "ANDROID!"
#define VENDOR_BOOT_MAGIC "VNDRBOOT"
#define CHROMEOS_MAGIC "CHROMEOS"
#define GZIP1_MAGIC "\x1f\x8b"
#define GZIP2_MAGIC "\x1f\x9e"
#define LZOP_MAGIC "\x89""LZO"
#define XZ_MAGIC "\xfd""7zXZ"
#define BZIP_MAGIC "BZh"
#define LZ4_LEG_MAGIC "\x02\x21\x4c\x18"
#define LZ41_MAGIC "\x03\x21\x4c\x18"
#define LZ42_MAGIC "\x04\x22\x4d\x18"
#define MTK_MAGIC "\x88\x16\x88\x58"
#define DTB_MAGIC "\xd0\x0d\xfe\xed"
#define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79"
#define DHTB_MAGIC "\x44\x48\x54\x42\x01\x00\x00\x00"
#define SEANDROID_MAGIC "SEANDROIDENFORCE"
#define TEGRABLOB_MAGIC "-SIGNED-BY-SIGNBLOB-"
#define NOOKHD_RL_MAGIC "Red Loader"
#define NOOKHD_GL_MAGIC "Green Loader"
#define NOOKHD_GR_MAGIC "Green Recovery"
#define NOOKHD_EB_MAGIC "eMMC boot.img+secondloader"
#define NOOKHD_ER_MAGIC "eMMC recovery.img+secondloader"
#define NOOKHD_PRE_HEADER_SZ 1048576
#define ACCLAIM_MAGIC "BauwksBoot"
#define ACCLAIM_PRE_HEADER_SZ 262144
#define AMONET_MICROLOADER_MAGIC "microloader"
#define AMONET_MICROLOADER_SZ 1024
#define AVB_FOOTER_MAGIC "AVBf"
#define AVB_MAGIC "AVB0"
#define ZIMAGE_MAGIC "\x18\x28\x6f\x01"
enum class FileFormat : ::std::uint8_t;
int unpack(rust::Utf8CStr image, bool skip_decomp = false, bool hdr = false);
void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp = false);
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp = false);
void cleanup();
FileFormat check_fmt(const void *buf, size_t len);
static inline FileFormat check_fmt(rust::Slice<const uint8_t> bytes) {
return check_fmt(bytes.data(), bytes.size());
}

View File

@@ -1,305 +0,0 @@
#include <base.hpp>
#include "boot-rs.hpp"
#include "magiskboot.hpp"
using namespace std;
static void print_formats() {
for (int fmt = +FileFormat::GZIP; fmt < +FileFormat::LZOP; ++fmt) {
fprintf(stderr, "%s ", fmt2name[(FileFormat) fmt]);
}
}
static void usage(char *arg0) {
fprintf(stderr,
R"EOF(MagiskBoot - Boot Image Modification Tool
Usage: %s <action> [args...]
Supported actions:
unpack [-n] [-h] <bootimg>
Unpack <bootimg> to its individual components, each component to
a file with its corresponding file name in the current directory.
Supported components: kernel, kernel_dtb, ramdisk.cpio, second,
dtb, extra, and recovery_dtbo.
By default, each component will be decompressed on-the-fly.
If '-n' is provided, all decompression operations will be skipped;
each component will remain untouched, dumped in its original format.
If '-h' is provided, the boot image header information will be
dumped to the file 'header', which can be used to modify header
configurations during repacking.
Return values:
0:valid 1:error 2:chromeos
repack [-n] <origbootimg> [outbootimg]
Repack boot image components using files from the current directory
to [outbootimg], or 'new-boot.img' if not specified. Current directory
should only contain required files for [outbootimg], or incorrect
[outbootimg] may be produced.
<origbootimg> is the original boot image used to unpack the components.
By default, each component will be automatically compressed using its
corresponding format detected in <origbootimg>. If a component file
in the current directory is already compressed, then no addition
compression will be performed for that specific component.
If '-n' is provided, all compression operations will be skipped.
If env variable PATCHVBMETAFLAG is set to true, all disable flags in
the boot image's vbmeta header will be set.
verify <bootimg> [x509.pem]
Check whether the boot image is signed with AVB 1.0 signature.
Optionally provide a certificate to verify whether the image is
signed by the public key certificate.
Return value:
0:valid 1:error
sign <bootimg> [name] [x509.pem pk8]
Sign <bootimg> with AVB 1.0 signature.
Optionally provide the name of the image (default: '/boot').
Optionally provide the certificate/private key pair for signing.
If the certificate/private key pair is not provided, the AOSP
verity key bundled in the executable will be used.
extract <payload.bin> [partition] [outfile]
Extract [partition] from <payload.bin> to [outfile].
If [outfile] is not specified, then output to '[partition].img'.
If [partition] is not specified, then attempt to extract either
'init_boot' or 'boot'. Which partition was chosen can be determined
by whichever 'init_boot.img' or 'boot.img' exists.
<payload.bin> can be '-' to be STDIN.
hexpatch <file> <hexpattern1> <hexpattern2>
Search <hexpattern1> in <file>, and replace it with <hexpattern2>
cpio <incpio> [commands...]
Do cpio commands to <incpio> (modifications are done in-place).
Each command is a single argument; add quotes for each command.
See "cpio --help" for supported commands.
dtb <file> <action> [args...]
Do dtb related actions to <file>.
See "dtb --help" for supported actions.
split [-n] <file>
Split image.*-dtb into kernel + kernel_dtb.
If '-n' is provided, decompression operations will be skipped;
the kernel will remain untouched, split in its original format.
sha1 <file>
Print the SHA1 checksum for <file>
cleanup
Cleanup the current working directory
compress[=format] <infile> [outfile]
Compress <infile> with [format] to [outfile].
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
If [format] is not specified, then gzip will be used.
If [outfile] is not specified, then <infile> will be replaced
with another file suffixed with a matching file extension.
Supported formats: )EOF", arg0);
print_formats();
fprintf(stderr, R"EOF(
decompress <infile> [outfile]
Detect format and decompress <infile> to [outfile].
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
If [outfile] is not specified, then <infile> will be replaced
with another file removing its archive format file extension.
Supported formats: )EOF");
print_formats();
fprintf(stderr, "\n\n");
exit(1);
}
static void decompress(char *infile, const char *outfile) {
bool in_std = infile == "-"sv;
bool rm_in = false;
int in_fd = in_std ? STDIN_FILENO : xopen(infile, O_RDONLY);
int out_fd = -1;
uint8_t buf[4096];
size_t len = read(in_fd, buf, sizeof(buf));
FileFormat type = check_fmt(buf, len);
fprintf(stderr, "Detected format: [%s]\n", fmt2name[type]);
if (!COMPRESSED(type))
LOGE("Input file is not a supported compressed type!\n");
// If user does not provide outfile, infile has to be either
// <path>.[ext], or '-'. Outfile will be either <path> or '-'.
// If the input does not have proper format, abort.
char *ext = nullptr;
if (outfile == nullptr) {
outfile = infile;
if (!in_std) {
ext = strrchr(infile, '.');
if (ext == nullptr || strcmp(ext, fmt2ext[type]) != 0)
LOGE("Input file is not a supported type!\n");
// Strip out extension and remove input
*ext = '\0';
rm_in = true;
fprintf(stderr, "Decompressing to [%s]\n", outfile);
}
}
out_fd = outfile == "-"sv ?
STDOUT_FILENO :
xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (ext) *ext = '.';
decompress_bytes_fd(type, byte_view{ buf, len }, in_fd, out_fd);
if (in_fd != STDIN_FILENO) close(in_fd);
if (out_fd != STDOUT_FILENO) close(out_fd);
if (rm_in)
unlink(infile);
}
static void compress(const char *method, const char *infile, const char *outfile) {
FileFormat fmt = name2fmt[method];
if (fmt == FileFormat::UNKNOWN)
LOGE("Unknown compression method: [%s]\n", method);
bool in_std = infile == "-"sv;
bool rm_in = false;
int in_fd = in_std ? STDIN_FILENO : xopen(infile, O_RDONLY);
int out_fd = -1;
if (outfile == nullptr) {
if (in_std) {
out_fd = STDOUT_FILENO;
} else {
// If user does not provide outfile and infile is not
// STDIN, output to <infile>.[ext]
string tmp(infile);
tmp += fmt2ext[fmt];
out_fd = xopen(tmp.data(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
fprintf(stderr, "Compressing to [%s]\n", tmp.data());
rm_in = true;
}
} else {
out_fd = outfile == "-"sv ?
STDOUT_FILENO :
xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
}
compress_fd(fmt, in_fd, out_fd);
if (in_fd != STDIN_FILENO) close(in_fd);
if (out_fd != STDOUT_FILENO) close(out_fd);
if (rm_in)
unlink(infile);
}
int main(int argc, char *argv[]) {
cmdline_logging();
umask(0);
if (argc < 2)
usage(argv[0]);
// Skip '--' for backwards compatibility
string_view action(argv[1]);
if (str_starts(action, "--"))
action = argv[1] + 2;
if (action == "cleanup") {
fprintf(stderr, "Cleaning up...\n");
unlink(HEADER_FILE);
unlink(KERNEL_FILE);
unlink(RAMDISK_FILE);
unlink(SECOND_FILE);
unlink(KER_DTB_FILE);
unlink(EXTRA_FILE);
unlink(RECV_DTBO_FILE);
unlink(DTB_FILE);
unlink(BOOTCONFIG_FILE);
rm_rf(VND_RAMDISK_DIR);
} else if (argc > 2 && action == "sha1") {
uint8_t sha1[20];
{
mmap_data m(argv[2]);
sha1_hash(m, byte_data(sha1, sizeof(sha1)));
}
for (uint8_t i : sha1)
printf("%02x", i);
printf("\n");
} else if (argc > 2 && action == "split") {
if (argv[2] == "-n"sv) {
if (argc == 3)
usage(argv[0]);
return split_image_dtb(argv[3], true);
} else {
return split_image_dtb(argv[2]);
}
} else if (argc > 2 && action == "unpack") {
int idx = 2;
bool nodecomp = false;
bool hdr = false;
for (;;) {
if (idx >= argc)
usage(argv[0]);
if (argv[idx][0] != '-')
break;
for (char *flag = &argv[idx][1]; *flag; ++flag) {
if (*flag == 'n')
nodecomp = true;
else if (*flag == 'h')
hdr = true;
else
usage(argv[0]);
}
++idx;
}
return unpack(argv[idx], nodecomp, hdr);
} else if (argc > 2 && action == "repack") {
if (argv[2] == "-n"sv) {
if (argc == 3)
usage(argv[0]);
repack(argv[3], argv[4] ? argv[4] : NEW_BOOT, true);
} else {
repack(argv[2], argv[3] ? argv[3] : NEW_BOOT);
}
} else if (argc > 2 && action == "verify") {
return verify(argv[2], argv[3]);
} else if (argc > 2 && action == "sign") {
if (argc == 5) usage(argv[0]);
return sign(
argv[2],
argc > 3 ? argv[3] : "/boot",
argc > 5 ? argv[4] : nullptr,
argc > 5 ? argv[5] : nullptr);
} else if (argc > 2 && action == "decompress") {
decompress(argv[2], argv[3]);
} else if (argc > 2 && str_starts(action, "compress")) {
compress(action[8] == '=' ? &action[9] : "gzip", argv[2], argv[3]);
} else if (argc > 4 && action == "hexpatch") {
return hexpatch(byte_view(argv[2]), byte_view(argv[3]), byte_view(argv[4])) ? 0 : 1;
} else if (argc > 2 && action == "cpio") {
return rust::cpio_commands(argc - 2, argv + 2) ? 0 : 1;
} else if (argc > 2 && action == "dtb") {
return rust::dtb_commands(argc - 2, argv + 2) ? 0 : 1;
} else if (argc > 2 && action == "extract") {
return rust::extract_boot_from_payload(
argv[2],
argc > 3 ? argv[3] : "",
argc > 4 ? argv[4] : ""
) ? 0 : 1;
} else {
usage(argv[0]);
}
return 0;
}

View File

@@ -45,7 +45,7 @@ fn remove_pattern(buf: &mut [u8], pattern_matcher: unsafe fn(&[u8]) -> Option<us
let skipped = buf.get_unchecked(read..(read + len));
// SAFETY: all matching patterns are ASCII bytes
let skipped = std::str::from_utf8_unchecked(skipped);
eprintln!("Remove pattern [{}]", skipped);
eprintln!("Remove pattern [{skipped}]");
sz -= len;
read += len;
} else {
@@ -102,19 +102,15 @@ fn hex2byte(hex: &[u8]) -> Vec<u8> {
v
}
pub fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool {
pub fn hexpatch(file: &mut String, from: &Utf8CStr, to: &Utf8CStr) -> bool {
let res: LoggedResult<bool> = try {
let file = Utf8CStr::from_bytes(file)?;
let from = Utf8CStr::from_bytes(from)?;
let to = Utf8CStr::from_bytes(to)?;
let mut map = MappedFile::open_rw(file)?;
let mut map = MappedFile::open_rw(Utf8CStr::from_string(file))?;
let pattern = hex2byte(from.as_bytes());
let patch = hex2byte(to.as_bytes());
let v = map.patch(pattern.as_slice(), patch.as_slice());
for off in &v {
eprintln!("Patch @ {:#010X} [{}] -> [{}]", off, from, to);
eprintln!("Patch @ {off:#010X} [{from}] -> [{to}]");
}
!v.is_empty()
};

View File

@@ -9,9 +9,7 @@ use std::{
use crate::compress::get_decoder;
use crate::ffi::check_fmt;
use crate::proto::update_metadata::{DeltaArchiveManifest, mod_InstallOperation::Type};
use base::{
LoggedError, LoggedResult, ReadSeekExt, ResultExt, Utf8CStr, WriteExt, error, ffi::Utf8CStrRef,
};
use base::{LoggedError, LoggedResult, ReadSeekExt, ResultExt, WriteExt, error};
macro_rules! bad_payload {
($msg:literal) => {{
@@ -26,15 +24,15 @@ macro_rules! bad_payload {
const PAYLOAD_MAGIC: &str = "CrAU";
fn do_extract_boot_from_payload(
in_path: &Utf8CStr,
partition_name: Option<&Utf8CStr>,
out_path: Option<&Utf8CStr>,
pub fn extract_boot_from_payload(
in_path: &str,
partition_name: Option<&str>,
out_path: Option<&str>,
) -> LoggedResult<()> {
let mut reader = BufReader::new(if in_path == "-" {
unsafe { File::from_raw_fd(0) }
} else {
File::open(in_path).log_with_msg(|w| write!(w, "Cannot open '{}'", in_path))?
File::open(in_path).log_with_msg(|w| write!(w, "Cannot open '{in_path}'"))?
});
let buf = &mut [0u8; 4];
@@ -107,7 +105,7 @@ fn do_extract_boot_from_payload(
};
let mut out_file =
File::create(out_path).log_with_msg(|w| write!(w, "Cannot write to '{}'", out_path))?;
File::create(out_path).log_with_msg(|w| write!(w, "Cannot write to '{out_path}'"))?;
// Skip the manifest signature
reader.skip(manifest_sig_len as usize)?;
@@ -179,25 +177,3 @@ fn do_extract_boot_from_payload(
Ok(())
}
pub fn extract_boot_from_payload(
in_path: Utf8CStrRef,
partition: Utf8CStrRef,
out_path: Utf8CStrRef,
) -> bool {
let res: LoggedResult<()> = try {
let partition = if partition.is_empty() {
None
} else {
Some(partition)
};
let out_path = if out_path.is_empty() {
None
} else {
Some(out_path)
};
do_extract_boot_from_payload(in_path, partition, out_path)?
};
res.log_with_msg(|w| w.write_str("Failed to extract from payload"))
.is_ok()
}

View File

@@ -25,8 +25,7 @@ use x509_cert::der::Any;
use x509_cert::der::asn1::{OctetString, PrintableString};
use x509_cert::spki::AlgorithmIdentifier;
use base::libc::c_char;
use base::{LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr, log_err};
use base::{LoggedResult, MappedFile, ResultExt, Utf8CStr, cstr, log_err};
use crate::ffi::BootImage;
@@ -117,7 +116,7 @@ impl Verifier {
digest = Box::<Sha512>::default();
VerifyingKey::SHA521withECDSA(ec)
} else {
return Err(log_err!("Unsupported private key"));
return log_err!("Unsupported private key");
};
Ok(Verifier { digest, key })
}
@@ -178,7 +177,7 @@ impl Signer {
SigningKey::SHA521withECDSA(ec)
}
_ => {
return Err(log_err!("Unsupported private key"));
return log_err!("Unsupported private key");
}
},
},
@@ -249,7 +248,7 @@ struct BootSignature {
impl BootSignature {
fn verify(self, payload: &[u8]) -> LoggedResult<()> {
if self.authenticated_attributes.length as usize != payload.len() {
return Err(log_err!("Invalid image size"));
return log_err!("Invalid image size");
}
let mut verifier = Verifier::from_public_key(
self.certificate
@@ -265,23 +264,27 @@ impl BootSignature {
}
}
pub fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool {
let res: LoggedResult<()> = try {
let tail = img.tail();
impl BootImage {
pub fn verify(&self, cert: Option<&Utf8CStr>) -> LoggedResult<()> {
let tail = self.tail();
if tail.starts_with(b"AVB0") {
return log_err!();
}
// Don't use BootSignature::from_der because tail might have trailing zeros
let mut reader = SliceReader::new(tail)?;
let mut sig = BootSignature::decode(&mut reader)?;
match unsafe { Utf8CStr::from_ptr(cert) } {
Ok(s) => {
let pem = MappedFile::open(s)?;
sig.certificate = Certificate::from_pem(pem)?;
}
Err(StrErr::NullPointerError) => {}
Err(e) => Err(e)?,
if let Some(s) = cert {
let pem = MappedFile::open(s)?;
sig.certificate = Certificate::from_pem(pem)?;
};
sig.verify(img.payload())?;
};
res.is_ok()
sig.verify(self.payload()).log()
}
pub fn verify_for_cxx(&self) -> bool {
self.verify(None).is_ok()
}
}
enum Bytes {
@@ -303,47 +306,44 @@ const VERITY_PK8: &[u8] = include_bytes!("../../../tools/keys/verity.pk8");
pub fn sign_boot_image(
payload: &[u8],
name: *const c_char,
cert: *const c_char,
key: *const c_char,
) -> Vec<u8> {
let res: LoggedResult<Vec<u8>> = try {
// Process arguments
let name = unsafe { Utf8CStr::from_ptr(name) }?;
let cert = match unsafe { Utf8CStr::from_ptr(cert) } {
Ok(s) => Bytes::Mapped(MappedFile::open(s)?),
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PEM),
Err(e) => Err(e)?,
};
let key = match unsafe { Utf8CStr::from_ptr(key) } {
Ok(s) => Bytes::Mapped(MappedFile::open(s)?),
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PK8),
Err(e) => Err(e)?,
};
// Parse cert and private key
let cert = Certificate::from_pem(cert)?;
let mut signer = Signer::from_private_key(key.as_ref())?;
// Sign image
let attr = AuthenticatedAttributes {
target: PrintableString::new(name.as_bytes())?,
length: payload.len() as u64,
};
signer.update(payload);
signer.update(attr.to_der()?.as_slice());
let sig = signer.sign()?;
// Create BootSignature DER
let alg_id = cert.signature_algorithm().clone();
let sig = BootSignature {
format_version: 1,
certificate: cert,
algorithm_identifier: alg_id,
authenticated_attributes: attr,
signature: OctetString::new(sig)?,
};
sig.to_der()?
name: &Utf8CStr,
cert: Option<&Utf8CStr>,
key: Option<&Utf8CStr>,
) -> LoggedResult<Vec<u8>> {
let cert = match cert {
Some(s) => Bytes::Mapped(MappedFile::open(s)?),
None => Bytes::Slice(VERITY_PEM),
};
res.unwrap_or_default()
let key = match key {
Some(s) => Bytes::Mapped(MappedFile::open(s)?),
None => Bytes::Slice(VERITY_PK8),
};
// Parse cert and private key
let cert = Certificate::from_pem(cert)?;
let mut signer = Signer::from_private_key(key.as_ref())?;
// Sign image
let attr = AuthenticatedAttributes {
target: PrintableString::new(name.as_bytes())?,
length: payload.len() as u64,
};
signer.update(payload);
signer.update(attr.to_der()?.as_slice());
let sig = signer.sign()?;
// Create BootSignature DER
let alg_id = cert.signature_algorithm().clone();
let sig = BootSignature {
format_version: 1,
certificate: cert,
algorithm_identifier: alg_id,
authenticated_attributes: attr,
signature: OctetString::new(sig)?,
};
sig.to_der().log()
}
pub fn sign_payload_for_cxx(payload: &[u8]) -> Vec<u8> {
sign_boot_image(payload, cstr!("/boot"), None, None).unwrap_or_default()
}

Some files were not shown because too many files have changed in this diff Show More