You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Compare commits
82 Commits
canary-281
...
canary-290
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1dc47a047 | ||
|
|
62b1310d97 | ||
|
|
0a86916d3a | ||
|
|
9907ce57aa | ||
|
|
b92626cacc | ||
|
|
5a762f0a8e | ||
|
|
5dd7a7d804 | ||
|
|
7831f40691 | ||
|
|
4f4b1ff885 | ||
|
|
97901979dd | ||
|
|
287316842c | ||
|
|
608786e8f3 | ||
|
|
9684a35cab | ||
|
|
e3e4202954 | ||
|
|
23c2054d46 | ||
|
|
a20a2a8fa0 | ||
|
|
a2896be4a6 | ||
|
|
e9220a28d9 | ||
|
|
cf12087e21 | ||
|
|
00c1b36837 | ||
|
|
03e034795d | ||
|
|
79c0fafe43 | ||
|
|
d499819ba0 | ||
|
|
86da917174 | ||
|
|
30bd7d6555 | ||
|
|
e5a12f0f5f | ||
|
|
c85a8434c6 | ||
|
|
427a1ca4e5 | ||
|
|
22884e173a | ||
|
|
d1829308e9 | ||
|
|
73840f8721 | ||
|
|
c7d1af9805 | ||
|
|
4ad26d3dfb | ||
|
|
0c70b7670c | ||
|
|
f44d044095 | ||
|
|
5c1cb13472 | ||
|
|
3327fc668e | ||
|
|
610945ac54 | ||
|
|
ddf5474917 | ||
|
|
6ba1685ade | ||
|
|
e02b5f7868 | ||
|
|
ab2e5d1e7e | ||
|
|
f3fef7bfe4 | ||
|
|
c34c7838bb | ||
|
|
c8a16b0e0c | ||
|
|
14f9ed91a1 | ||
|
|
7a207d4ccf | ||
|
|
92a42d901f | ||
|
|
084d89fcce | ||
|
|
55b036c071 | ||
|
|
30e79310ab | ||
|
|
f063fa5054 | ||
|
|
7bd901273c | ||
|
|
c1e061603b | ||
|
|
cb08504fe5 | ||
|
|
c0a1fb77be | ||
|
|
4864c1112a | ||
|
|
9ddeab034b | ||
|
|
c4847ed288 | ||
|
|
b8f1523fb2 | ||
|
|
fb7fa8a6b3 | ||
|
|
9c7d359093 | ||
|
|
eb54bc1fd7 | ||
|
|
d4a0286e13 | ||
|
|
83e66767ff | ||
|
|
7dc010749b | ||
|
|
8e8d013b1b | ||
|
|
bba0373808 | ||
|
|
1fa318dc8c | ||
|
|
6edc5e2037 | ||
|
|
1523ed9f78 | ||
|
|
8e604d2ab8 | ||
|
|
2aba7247a9 | ||
|
|
e66fe8533e | ||
|
|
b03fbb3917 | ||
|
|
c2ece62e4c | ||
|
|
8c972dcf34 | ||
|
|
50af14f2a3 | ||
|
|
e0a356b319 | ||
|
|
c09a792958 | ||
|
|
0bbfe7f44d | ||
|
|
a396abf565 |
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -12,7 +12,8 @@
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
tools/** binary
|
||||
tools/rustup_wrapper/** -binary
|
||||
tools/rustup-wrapper/** -binary
|
||||
tools/elf-cleaner/** -binary
|
||||
*.jar binary
|
||||
*.exe binary
|
||||
*.apk binary
|
||||
|
||||
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@@ -6,10 +6,7 @@ on:
|
||||
paths:
|
||||
- "app/**"
|
||||
- "native/**"
|
||||
- "buildSrc/**"
|
||||
- "build.py"
|
||||
- "gradle.properties"
|
||||
- "gradle/libs.versions.toml"
|
||||
- ".github/workflows/build.yml"
|
||||
pull_request:
|
||||
branches: [master]
|
||||
@@ -39,7 +36,7 @@ jobs:
|
||||
run: ./build.py -v all
|
||||
|
||||
- name: Stop gradle daemon
|
||||
run: ./gradlew --stop
|
||||
run: ./app/gradlew --stop
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -75,7 +72,7 @@ jobs:
|
||||
run: python build.py -v -c .github/ci.prop all
|
||||
|
||||
- name: Stop gradle daemon
|
||||
run: ./gradlew --stop
|
||||
run: ./app/gradlew --stop
|
||||
|
||||
avd-test:
|
||||
name: Test API ${{ matrix.version }} (x86_64)
|
||||
@@ -88,9 +85,9 @@ jobs:
|
||||
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
|
||||
type: [""]
|
||||
include:
|
||||
- version: "Baklava"
|
||||
- version: 36
|
||||
type: "google_apis"
|
||||
- version: "Baklava"
|
||||
- version: 36
|
||||
type: "google_apis_ps16k"
|
||||
|
||||
steps:
|
||||
@@ -177,8 +174,8 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- branch: "aosp-main"
|
||||
device: "aosp_cf_x86_64_phone"
|
||||
- branch: "aosp-android-latest-release"
|
||||
device: "aosp_cf_x86_64_only_phone"
|
||||
|
||||
steps:
|
||||
- name: Check out
|
||||
@@ -197,7 +194,7 @@ jobs:
|
||||
|
||||
- name: Run Cuttlefish test
|
||||
timeout-minutes: 10
|
||||
run: su $USER -c 'scripts/cuttlefish.sh test'
|
||||
run: sudo -E -u $USER scripts/cuttlefish.sh test
|
||||
|
||||
- name: Upload logs on error
|
||||
if: ${{ failure() }}
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -2,19 +2,13 @@ out
|
||||
*.zip
|
||||
*.jks
|
||||
*.apk
|
||||
*.log
|
||||
/config.prop
|
||||
/notes.md
|
||||
/update.sh
|
||||
/app/dict.txt
|
||||
|
||||
# Built binaries
|
||||
native/out
|
||||
|
||||
# Android Studio / Gradle
|
||||
# Android Studio
|
||||
*.iml
|
||||
.gradle
|
||||
.idea
|
||||
.kotlin
|
||||
/local.properties
|
||||
/build
|
||||
/captures
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -25,6 +25,3 @@
|
||||
[submodule "crt0"]
|
||||
path = native/src/external/crt0
|
||||
url = https://github.com/topjohnwu/crt0.git
|
||||
[submodule "termux-elf-cleaner"]
|
||||
path = tools/termux-elf-cleaner
|
||||
url = https://github.com/termux/termux-elf-cleaner.git
|
||||
|
||||
@@ -21,8 +21,8 @@ Some highlight features:
|
||||
Click the icon below to download Magisk apk.
|
||||
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-28103)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v29.0)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-29001)
|
||||
|
||||
## Useful Links
|
||||
|
||||
|
||||
7
app/.gitignore
vendored
Normal file
7
app/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/dict.txt
|
||||
|
||||
# Gradle
|
||||
.gradle
|
||||
.kotlin
|
||||
/local.properties
|
||||
/build
|
||||
@@ -35,7 +35,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":app:core"))
|
||||
implementation(project(":core"))
|
||||
coreLibraryDesugaring(libs.jdk.libs)
|
||||
|
||||
implementation(libs.indeterminate.checkbox)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.view.KeyEvent
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDirections
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.navOptions
|
||||
import com.topjohnwu.magisk.utils.AccessibilityUtils
|
||||
|
||||
abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Binding>() {
|
||||
|
||||
@@ -31,7 +34,17 @@ abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Bindin
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun navigate(directions: NavDirections, navigation: NavController, cr: ContentResolver) {
|
||||
if (AccessibilityUtils.isAnimationEnabled(cr)) {
|
||||
navigation.navigate(directions)
|
||||
} else {
|
||||
navigation.navigate(directions, navOptions {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun NavDirections.navigate() {
|
||||
navigation.navigate(this)
|
||||
navigate(this, navigation, contentResolver)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.download.DownloadEngine
|
||||
import com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding
|
||||
import com.topjohnwu.magisk.core.R as CoreR
|
||||
import androidx.navigation.findNavController
|
||||
import com.topjohnwu.magisk.arch.NavigationActivity
|
||||
|
||||
class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
|
||||
|
||||
@@ -68,7 +70,13 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
|
||||
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_settings ->
|
||||
HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
|
||||
activity?.let {
|
||||
NavigationActivity.navigate(
|
||||
HomeFragmentDirections.actionHomeFragmentToSettingsFragment(),
|
||||
it.findNavController(R.id.main_nav_host),
|
||||
it.contentResolver,
|
||||
)
|
||||
}
|
||||
R.id.action_reboot -> activity?.let { RebootMenu.inflate(it).show() }
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.HorizontalScrollView
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.isVisible
|
||||
import com.topjohnwu.magisk.R
|
||||
@@ -12,6 +13,7 @@ import com.topjohnwu.magisk.arch.BaseFragment
|
||||
import com.topjohnwu.magisk.arch.viewModel
|
||||
import com.topjohnwu.magisk.databinding.FragmentLogMd2Binding
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.utils.AccessibilityUtils
|
||||
import com.topjohnwu.magisk.utils.MotionRevealHelper
|
||||
import rikka.recyclerview.addEdgeSpacing
|
||||
import rikka.recyclerview.addItemSpacing
|
||||
@@ -56,6 +58,11 @@ class LogFragment : BaseFragment<FragmentLogMd2Binding>(), MenuProvider {
|
||||
addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1)
|
||||
fixEdgeEffect()
|
||||
}
|
||||
|
||||
if (!AccessibilityUtils.isAnimationEnabled(requireContext().contentResolver)) {
|
||||
val scrollView = view.findViewById<HorizontalScrollView>(R.id.log_scroll_magisk)
|
||||
scrollView.setOverScrollMode(View.OVER_SCROLL_NEVER)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.provider.Settings
|
||||
|
||||
class AccessibilityUtils {
|
||||
companion object {
|
||||
fun isAnimationEnabled(cr: ContentResolver): Boolean {
|
||||
return !(Settings.Global.getFloat(cr, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f) == 0.0f
|
||||
&& Settings.Global.getFloat(cr, Settings.Global.TRANSITION_ANIMATION_SCALE, 1.0f) == 0.0f
|
||||
&& Settings.Global.getFloat(cr, Settings.Global.WINDOW_ANIMATION_SCALE, 1.0f) == 0.0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/log_scroll_magisk"
|
||||
gone="@{viewModel.loading}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
tasks.register("clean") {
|
||||
plugins {
|
||||
id("MagiskPlugin")
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete::class) {
|
||||
delete(rootProject.layout.buildDirectory)
|
||||
|
||||
subprojects.forEach {
|
||||
dependsOn(":app:${it.name}:clean")
|
||||
dependsOn(":${it.name}:clean")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ private val defaultAbis = setOf("armeabi-v7a", "x86", "arm64-v8a", "x86_64")
|
||||
object Config {
|
||||
operator fun get(key: String): String? {
|
||||
val v = props[key] as? String ?: return null
|
||||
return if (v.isBlank()) null else v
|
||||
return v.ifBlank { null }
|
||||
}
|
||||
|
||||
fun contains(key: String) = get(key) != null
|
||||
@@ -28,19 +28,21 @@ object Config {
|
||||
}
|
||||
}
|
||||
|
||||
val Project.baseDir: File get() = rootProject.file("..")
|
||||
|
||||
class MagiskPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) = project.applyPlugin()
|
||||
|
||||
private fun Project.applyPlugin() {
|
||||
initRandom(rootProject.file("app/dict.txt"))
|
||||
initRandom(rootProject.file("dict.txt"))
|
||||
props.clear()
|
||||
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
|
||||
val configPath: String? by this
|
||||
val config = configPath?.let { File(it) } ?: rootProject.file("config.prop")
|
||||
val config = configPath?.let { File(it) } ?: File(baseDir, "config.prop")
|
||||
if (config.exists())
|
||||
config.inputStream().use { props.load(it) }
|
||||
|
||||
val repo = FileRepository(rootProject.file(".git"))
|
||||
val repo = FileRepository(File(baseDir, ".git"))
|
||||
val refId = repo.refDatabase.exactRef("HEAD").objectId
|
||||
commitHash = repo.newObjectReader().abbreviate(refId, 8).name()
|
||||
}
|
||||
@@ -71,8 +71,8 @@ private val Project.androidComponents
|
||||
|
||||
fun Project.setupCommon() {
|
||||
androidBase {
|
||||
compileSdkVersion(35)
|
||||
buildToolsVersion = "35.0.1"
|
||||
compileSdkVersion(36)
|
||||
buildToolsVersion = "36.0.0"
|
||||
ndkPath = "$sdkDirectory/ndk/magisk"
|
||||
ndkVersion = "29.0.13113456"
|
||||
|
||||
@@ -89,6 +89,7 @@ fun Project.setupCommon() {
|
||||
resources {
|
||||
excludes += arrayOf(
|
||||
"/META-INF/*",
|
||||
"/META-INF/androidx/**",
|
||||
"/META-INF/versions/**",
|
||||
"/org/bouncycastle/**",
|
||||
"/org/apache/commons/**",
|
||||
@@ -150,7 +151,7 @@ fun Project.setupCoreLib() {
|
||||
into("src/main/jniLibs")
|
||||
for (abi in abiList) {
|
||||
into(abi) {
|
||||
from(rootProject.file("native/out/$abi")) {
|
||||
from(File(baseDir, "native/out/$abi")) {
|
||||
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
|
||||
rename { if (it.endsWith(".so")) it else "lib$it.so" }
|
||||
}
|
||||
@@ -172,10 +173,10 @@ fun Project.setupCoreLib() {
|
||||
|
||||
val syncResources by tasks.registering(Sync::class) {
|
||||
into("src/main/resources/META-INF/com/google/android")
|
||||
from(rootProject.file("scripts/update_binary.sh")) {
|
||||
from(File(baseDir, "scripts/update_binary.sh")) {
|
||||
rename { "update-binary" }
|
||||
}
|
||||
from(rootProject.file("scripts/flash_script.sh")) {
|
||||
from(File(baseDir, "scripts/flash_script.sh")) {
|
||||
rename { "updater-script" }
|
||||
}
|
||||
}
|
||||
@@ -186,7 +187,7 @@ fun Project.setupCoreLib() {
|
||||
tasks.getByPath("merge${variantCapped}JniLibFolders").dependsOn(downloadBusybox)
|
||||
processJavaResourcesProvider.configure { dependsOn(syncResources) }
|
||||
|
||||
val stubTask = tasks.getByPath(":app:stub:comment$variantCapped")
|
||||
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
|
||||
val stubApk = stubTask.outputs.files.asFileTree.filter {
|
||||
it.name.endsWith(".apk")
|
||||
}
|
||||
@@ -196,14 +197,14 @@ fun Project.setupCoreLib() {
|
||||
inputs.property("version", Config.version)
|
||||
inputs.property("versionCode", Config.versionCode)
|
||||
into("src/${this@all.name}/assets")
|
||||
from(rootProject.file("scripts")) {
|
||||
from(File(baseDir, "scripts")) {
|
||||
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
|
||||
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
|
||||
}
|
||||
from(rootProject.file("tools/bootctl"))
|
||||
from(File(baseDir, "tools/bootctl"))
|
||||
into("chromeos") {
|
||||
from(rootProject.file("tools/futility"))
|
||||
from(rootProject.file("tools/keys")) {
|
||||
from(File(baseDir, "tools/futility"))
|
||||
from(File(baseDir, "tools/keys")) {
|
||||
include("kernel_data_key.vbprivk", "kernel.keyblock")
|
||||
}
|
||||
}
|
||||
@@ -293,7 +294,7 @@ fun Project.setupAppCommon() {
|
||||
signingConfigs {
|
||||
create("config") {
|
||||
Config["keyStore"]?.also {
|
||||
storeFile = rootProject.file(it)
|
||||
storeFile = File(baseDir, it)
|
||||
storePassword = Config["keyStorePass"]
|
||||
keyAlias = Config["keyAlias"]
|
||||
keyPassword = Config["keyPass"]
|
||||
@@ -302,7 +303,7 @@ fun Project.setupAppCommon() {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
targetSdk = 35
|
||||
targetSdk = 36
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt")
|
||||
)
|
||||
@@ -29,7 +29,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":app:shared"))
|
||||
api(project(":shared"))
|
||||
|
||||
api(libs.timber)
|
||||
api(libs.markwon.core)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<application
|
||||
android:name=".App"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:multiArch="true"
|
||||
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"
|
||||
tools:remove="android:appComponentFactory">
|
||||
|
||||
|
||||
@@ -132,7 +132,6 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
if (entry != null) {
|
||||
val magisk32 = File(installDir, "magisk32")
|
||||
zf.getInputStream(entry).writeTo(magisk32)
|
||||
magisk32.setExecutable(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,11 +146,15 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
Os.symlink(lib.path, "$installDir/$name")
|
||||
}
|
||||
|
||||
// Also symlink magisk32 on 64-bit devices that supports 32-bit
|
||||
val lib32 = info.javaClass.getDeclaredField("secondaryNativeLibraryDir")
|
||||
.get(info) as String?
|
||||
if (lib32 != null) {
|
||||
Os.symlink("$lib32/libmagisk.so", "$installDir/magisk32");
|
||||
// Also extract magisk32 on 64-bit devices that supports 32-bit
|
||||
val abi32 = Const.CPU_ABI_32
|
||||
if (Process.is64Bit() && abi32 != null) {
|
||||
val name = "lib/$abi32/libmagisk.so"
|
||||
val entry = javaClass.classLoader!!.getResourceAsStream(name)
|
||||
if (entry != null) {
|
||||
val magisk32 = File(installDir, "magisk32")
|
||||
entry.writeTo(magisk32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@ import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Until
|
||||
import com.topjohnwu.magisk.core.model.module.LocalModule
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.EMPTY_ZYGISK
|
||||
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.superuser.Shell
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
@@ -49,8 +55,9 @@ class AdditionalTest : BaseTest {
|
||||
|
||||
@Test
|
||||
fun testModuleCount() {
|
||||
var expected = 0
|
||||
if (Environment.testModules()) expected +=2
|
||||
var expected = 2
|
||||
if (Environment.mount()) expected++
|
||||
if (Environment.preinit()) expected++
|
||||
if (Environment.lsposed()) expected++
|
||||
if (Environment.shamiko()) expected++
|
||||
assertEquals("Module count incorrect", expected, modules.size)
|
||||
@@ -78,11 +85,10 @@ class AdditionalTest : BaseTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModule01() {
|
||||
assumeTrue(Environment.testModules())
|
||||
fun testModuleMount() {
|
||||
assumeTrue(Environment.mount())
|
||||
|
||||
val module = modules.find { it.id == "test_01" }
|
||||
assertNotNull("test_01 is not installed", module)
|
||||
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()
|
||||
@@ -91,24 +97,42 @@ class AdditionalTest : BaseTest {
|
||||
"/system/bin/screenrecord should not exist",
|
||||
RootUtils.fs.getFile("/system/bin/screenrecord").exists()
|
||||
)
|
||||
module!!
|
||||
assertTrue("test_01 should be zygisk unloaded", module.zygiskUnloaded)
|
||||
val egg = RootUtils.fs.getFile("/system/app/EasterEgg").list() ?: arrayOf()
|
||||
assertTrue(
|
||||
"/system/app/EasterEgg should be empty",
|
||||
egg.isEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModule02() {
|
||||
assumeTrue(Environment.testModules())
|
||||
fun testSepolicyRule() {
|
||||
assumeTrue(Environment.preinit())
|
||||
|
||||
val module = modules.find { it.id == "test_02" }
|
||||
assertNotNull("test_02 is not installed", module)
|
||||
module!!
|
||||
assertTrue("test_02 should be zygisk unloaded", module.zygiskUnloaded)
|
||||
assertNotNull("$SEPOLICY_RULE is not installed", modules.find { it.id == SEPOLICY_RULE })
|
||||
assertTrue(
|
||||
"Module sepolicy.rule is not applied",
|
||||
Shell.cmd("magiskpolicy --print-rules | grep -q magisk_test").exec().isSuccess
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModule03() {
|
||||
assumeTrue(Environment.testModules())
|
||||
fun testEmptyZygiskModule() {
|
||||
val module = modules.find { it.id == EMPTY_ZYGISK }
|
||||
assertNotNull("$EMPTY_ZYGISK is not installed", module)
|
||||
module!!
|
||||
assertTrue("$EMPTY_ZYGISK should be zygisk unloaded", module.zygiskUnloaded)
|
||||
}
|
||||
|
||||
assertNull("test_03 should be removed", modules.find { it.id == "test_03" })
|
||||
@Test
|
||||
fun testInvalidZygiskModule() {
|
||||
val module = modules.find { it.id == INVALID_ZYGISK }
|
||||
assertNotNull("$INVALID_ZYGISK is not installed", module)
|
||||
module!!
|
||||
assertTrue("$INVALID_ZYGISK should be zygisk unloaded", module.zygiskUnloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveModule() {
|
||||
assertNull("$REMOVE_TEST should be removed", modules.find { it.id == REMOVE_TEST })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
|
||||
@Keep
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@@ -39,11 +40,16 @@ class Environment : BaseTest {
|
||||
fun before() = BaseTest.prerequisite()
|
||||
|
||||
// The kernel running on emulators < API 26 does not play well with
|
||||
// magic mount. Skip module tests on those legacy platforms.
|
||||
fun testModules(): Boolean {
|
||||
// magic mount. Skip mount_test on those legacy platforms.
|
||||
fun mount(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= 26
|
||||
}
|
||||
|
||||
// It is possible that there are no suitable preinit partition to use
|
||||
fun preinit(): Boolean {
|
||||
return Shell.cmd("magisk --preinit-device").exec().isSuccess
|
||||
}
|
||||
|
||||
fun lsposed(): Boolean {
|
||||
return Build.VERSION.SDK_INT in 27..34
|
||||
}
|
||||
@@ -53,6 +59,11 @@ class Environment : BaseTest {
|
||||
}
|
||||
|
||||
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 EMPTY_ZYGISK = "empty_zygisk"
|
||||
}
|
||||
|
||||
object TimberLog : CallbackList<String>(Runnable::run) {
|
||||
@@ -83,30 +94,56 @@ class Environment : BaseTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupModule01(root: ExtendedFile) {
|
||||
val error = "test_01 setup failed"
|
||||
val path = root.getChildFile("test_01")
|
||||
private fun setupMountTest(root: ExtendedFile) {
|
||||
val error = "$MOUNT_TEST setup failed"
|
||||
val path = root.getChildFile(MOUNT_TEST)
|
||||
|
||||
// Create /system/etc/newfile
|
||||
val etc = path.getChildFile("system").getChildFile("etc")
|
||||
assertTrue(error, etc.mkdirs())
|
||||
assertTrue(error, etc.getChildFile("newfile").createNewFile())
|
||||
|
||||
// Create /system/app/EasterEgg/.replace
|
||||
val egg = path.getChildFile("system").getChildFile("app").getChildFile("EasterEgg")
|
||||
assertTrue(error, egg.mkdirs())
|
||||
assertTrue(error, egg.getChildFile(".replace").createNewFile())
|
||||
|
||||
// Delete /system/bin/screenrecord
|
||||
val bin = path.getChildFile("system").getChildFile("bin")
|
||||
assertTrue(error, bin.mkdirs())
|
||||
assertTrue(error, Shell.cmd("mknod $bin/screenrecord c 0 0").exec().isSuccess)
|
||||
|
||||
// Create an empty zygisk folder
|
||||
val module = LocalModule(path)
|
||||
assertTrue(error, module.zygiskFolder.mkdir())
|
||||
|
||||
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupModule02(root: ExtendedFile) {
|
||||
val error = "test_02 setup failed"
|
||||
val path = root.getChildFile("test_02")
|
||||
private fun setupSepolicyRuleModule(root: ExtendedFile) {
|
||||
val error = "$SEPOLICY_RULE setup failed"
|
||||
val path = root.getChildFile(SEPOLICY_RULE)
|
||||
assertTrue(error, path.mkdirs())
|
||||
|
||||
// Add sepolicy patch
|
||||
PrintStream(path.getChildFile("sepolicy.rule").newOutputStream()).use {
|
||||
it.println("type magisk_test domain")
|
||||
}
|
||||
|
||||
assertTrue(error, Shell.cmd(
|
||||
"set_default_perm $path",
|
||||
"copy_preinit_files"
|
||||
).exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupEmptyZygiskModule(root: ExtendedFile) {
|
||||
val error = "$EMPTY_ZYGISK setup failed"
|
||||
val path = root.getChildFile(EMPTY_ZYGISK)
|
||||
|
||||
// Create an empty zygisk folder
|
||||
val module = LocalModule(path)
|
||||
assertTrue(error, module.zygiskFolder.mkdirs())
|
||||
}
|
||||
|
||||
private fun setupInvalidZygiskModule(root: ExtendedFile) {
|
||||
val error = "$INVALID_ZYGISK setup failed"
|
||||
val path = root.getChildFile(INVALID_ZYGISK)
|
||||
|
||||
// Create invalid zygisk libraries
|
||||
val module = LocalModule(path)
|
||||
@@ -119,9 +156,9 @@ class Environment : BaseTest {
|
||||
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupModule03(root: ExtendedFile) {
|
||||
val error = "test_03 setup failed"
|
||||
val path = root.getChildFile("test_03")
|
||||
private fun setupRemoveModule(root: ExtendedFile) {
|
||||
val error = "$REMOVE_TEST setup failed"
|
||||
val path = root.getChildFile(REMOVE_TEST)
|
||||
|
||||
// Create a new module but mark is as "remove"
|
||||
val module = LocalModule(path)
|
||||
@@ -175,12 +212,12 @@ class Environment : BaseTest {
|
||||
}
|
||||
}
|
||||
|
||||
if (testModules()) {
|
||||
val root = RootUtils.fs.getFile(Const.MODULE_PATH)
|
||||
setupModule01(root)
|
||||
setupModule02(root)
|
||||
setupModule03(root)
|
||||
}
|
||||
val root = RootUtils.fs.getFile(Const.MODULE_PATH)
|
||||
if (mount()) { setupMountTest(root) }
|
||||
if (preinit()) { setupSepolicyRuleModule(root) }
|
||||
setupEmptyZygiskModule(root)
|
||||
setupInvalidZygiskModule(root)
|
||||
setupRemoveModule(root)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -6,25 +6,25 @@
|
||||
<string name="logs">السجلات</string>
|
||||
<string name="settings">الإعدادات</string>
|
||||
<string name="install">تثبيت</string>
|
||||
<string name="section_home">الأساسي</string>
|
||||
<string name="section_theme">السِمات</string>
|
||||
<string name="section_home">الصفحة الرئيسية</string>
|
||||
<string name="section_theme">المظهر</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">لا يوجد إتصال</string>
|
||||
<string name="app_changelog">تفاصيل التحديث</string>
|
||||
<string name="loading">جارٍ التحميل...</string>
|
||||
<string name="update">تحديث</string>
|
||||
<string name="not_available">غير/متوفر</string>
|
||||
<string name="not_available">غير متوفر</string>
|
||||
<string name="hide">إخفاء</string>
|
||||
<string name="home_package">الحزمة</string>
|
||||
|
||||
<string name="home_support_title">تبرع لنا</string>
|
||||
<string name="home_item_source">الكود المصدري للتطبيق</string>
|
||||
<string name="home_support_content">مـاجـيسك هي، وستظل دوماً، مجانيةً و مفتوحة المصدر، اظهر اهتمامك لنا لكي نبقيها هكذا بدعم مالي صغير</string>
|
||||
<string name="home_support_content">ماجيسك هو، وسيظل دوماً، مجانياّ و مفتوح المصدر، اظهر اهتمامك لنا لكي نبقيه هكذا بدعم مالي صغير</string>
|
||||
<string name="home_installed_version">تم التثبيت</string>
|
||||
<string name="home_latest_version">آخر إصدار</string>
|
||||
<string name="invalid_update_channel">مصدر التحديث غير صالح</string>
|
||||
<string name="uninstall_magisk_title">إلغاء تثبيت Magisk</string>
|
||||
<string name="uninstall_magisk_title">إلغاء تثبيت ماجيسك</string>
|
||||
<string name="uninstall_magisk_msg">ستُعطل/ستُحذف جميع الإضافات. سيُحذف الروت، وربما ستشفر بياناتك إذا لم تكن غير مشفرة حالياً.</string>
|
||||
|
||||
<!--Install-->
|
||||
@@ -34,7 +34,7 @@
|
||||
<string name="install_options_title">الخيارات</string>
|
||||
<string name="install_method_title">الطريقة</string>
|
||||
<string name="install_next">التالي</string>
|
||||
<string name="install_start">هيا، بنا</string>
|
||||
<string name="install_start">هيا بنا</string>
|
||||
<string name="manager_download_install">اضغط للتنزيل و التثبيت</string>
|
||||
<string name="direct_install">تثبيت مباشر (موصى بها)</string>
|
||||
<string name="install_inactive_slot">التثبيت على المنطقة الغير نشطة (بعد OTA)</string>
|
||||
@@ -87,15 +87,15 @@
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">إظهار برامج النظام</string>
|
||||
<string name="hide_filter_hint">البحث بالإسم</string>
|
||||
<string name="hide_filter_hint">البحث بالاسم</string>
|
||||
<string name="hide_search">ابحث</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(لم توفر معلومات)</string>
|
||||
<string name="no_info_provided">(لا تتوفر معلومات)</string>
|
||||
<string name="reboot_recovery">إعادة التشغيل إلى Recovery</string>
|
||||
<string name="reboot_bootloader">إعادة التشغيل إلى Bootloader</string>
|
||||
<string name="reboot_download">إعادة التشغيل إلى وضـع Odin</string>
|
||||
<string name="reboot_edl">إعادة التشغيل إلى وضـعية EDL</string>
|
||||
<string name="reboot_edl">إعادة التشغيل إلى EDL</string>
|
||||
<string name="module_version_author">%1$sبواسطة%2$s</string>
|
||||
<string name="module_state_remove">إزالة </string>
|
||||
<string name="module_state_restore">إسترجاع</string>
|
||||
@@ -104,15 +104,15 @@
|
||||
<string name="external_rw_permission_denied">امنحني إذن الولوج للذاكرة الداخلية</string>
|
||||
|
||||
<!--Settings -->
|
||||
<string name="settings_dark_mode_title">وضـعية الِسمات</string>
|
||||
<string name="settings_dark_mode_message">حدد الوضـع الذي يناسب ذوقك</string>
|
||||
<string name="settings_dark_mode_title">المظهر</string>
|
||||
<string name="settings_dark_mode_message">حدد المظهر الذي يناسب ذوقك</string>
|
||||
<string name="settings_dark_mode_light">الوضـع المضيء</string>
|
||||
<string name="settings_dark_mode_system">اتبّع النظام</string>
|
||||
<string name="settings_dark_mode_dark">وضـع الظلام</string>
|
||||
<string name="settings_dark_mode_dark">الوضع المظلم</string>
|
||||
<string name="settings_download_path_title">مسار التحميل</string>
|
||||
<string name="settings_download_path_message">ستحمل الملفات إلى %1$s</string>
|
||||
<string name="language">اللغة</string>
|
||||
<string name="system_default">(الأفتراضي)</string>
|
||||
<string name="system_default">(الإفتراضي)</string>
|
||||
<string name="settings_check_update_title">تحقق من التحديثات</string>
|
||||
<string name="settings_check_update_summary">التحقق من التحديثات في الخلفية بشكل دوري</string>
|
||||
<string name="settings_update_channel_title">مصدر التحديثات</string>
|
||||
@@ -123,8 +123,8 @@
|
||||
<string name="settings_hosts_title">موانع الاعلانات</string>
|
||||
<string name="settings_hosts_summary">حجب الاعلانات دون تعديل النظام</string>
|
||||
<string name="settings_hosts_toast">تم تمكين خاصية حجب الاعلانات</string>
|
||||
<string name="settings_app_name_hint">الإسم الجديد</string>
|
||||
<string name="settings_app_name_helper">التطبيق الجديد سوف يملك هذا الإسم</string>
|
||||
<string name="settings_app_name_hint">الاسم الجديد</string>
|
||||
<string name="settings_app_name_helper">التطبيق الجديد سوف يملك هذا الاسم</string>
|
||||
<string name="settings_app_name_error">الصيغة غير مقبولة</string>
|
||||
<string name="settings_su_app_adb">التطبيقات و ADB</string>
|
||||
<string name="settings_su_app">التطبيقات فقط</string>
|
||||
@@ -140,51 +140,51 @@
|
||||
<string name="auto_response">الفعل التلقائي</string>
|
||||
<string name="request_timeout">المهلة قبل الفعل التلقائي</string>
|
||||
<string name="superuser_notification">إشعارات طلبات الروت</string>
|
||||
<string name="settings_su_reauth_title">إعادة المصادقة بعد الترقية</string>
|
||||
<string name="settings_su_reauth_summary">أعد مصادقة صلاحيات الروت بعد إجراء ترقيات للتطبيق</string>
|
||||
<string name="settings_su_reauth_title">إعادة المصادقة بعد التحديث</string>
|
||||
<string name="settings_su_reauth_summary">أعد مصادقة صلاحيات الروت بعد تحديث التطبيق</string>
|
||||
<string name="settings_customization">تخصيص</string>
|
||||
|
||||
<string name="multiuser_mode">نمط مستخدمين متعددين</string>
|
||||
<string name="multiuser_mode">نمط المستخدم المزدوج</string>
|
||||
<string name="settings_owner_only">مالك الجهاز فقط</string>
|
||||
<string name="settings_owner_manage">المالك هو من يحدد</string>
|
||||
<string name="settings_user_independent">مستقل </string>
|
||||
<string name="settings_user_independent">مستقل</string>
|
||||
<string name="owner_only_summary">للمالك فقط له صلاحيات الروت</string>
|
||||
<string name="owner_manage_summary">فقط المالك من يرفض و يمنح صلاحيات الروت</string>
|
||||
<string name="user_independent_summary">كل مستخدم له قواعد روت خاصة به</string>
|
||||
|
||||
<string name="mount_namespace_mode">نمط Mount Namespace</string>
|
||||
<string name="settings_ns_global">نمط Namespace العام</string>
|
||||
<string name="settings_ns_requester">نمط NameSpace المتوارث</string>
|
||||
<string name="settings_ns_isolate">نمط NameSpace المعزول</string>
|
||||
<string name="settings_ns_global">نمط Namespace عام</string>
|
||||
<string name="settings_ns_requester">نمط NameSpace متوارث</string>
|
||||
<string name="settings_ns_isolate">نمط NameSpace معزول</string>
|
||||
<string name="global_summary">جميع الجلسات الروت تستخدم NameSpace العام</string>
|
||||
<string name="requester_summary">جميع الجلسات الروت تستخدم NameSpace المتوارث</string>
|
||||
<string name="isolate_summary">جميع الجلسات الروت تستخدم NameSpace المعزول</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">تحديثات Magisk</string>
|
||||
<string name="update_channel">تحديثات ماجيسك</string>
|
||||
<string name="progress_channel">إشعارات التقدم</string>
|
||||
<string name="download_complete">اكتمل التنزيل</string>
|
||||
<string name="download_file_error">فشل تنزيل الملف</string>
|
||||
<string name="magisk_update_title">تحديث مـاجـيسك متوفر!</string>
|
||||
<string name="magisk_update_title">تحديث ماجيسك متوفر!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">نعم</string>
|
||||
<string name="no">لا</string>
|
||||
<string name="download">تنزيل</string>
|
||||
<string name="reboot">إعادة التشغيل</string>
|
||||
<string name="release_notes">معلومات الأصدار الجديد</string>
|
||||
<string name="flashing">يتم الحرق...</string>
|
||||
<string name="release_notes">معلومات الإصدار الجديد</string>
|
||||
<string name="flashing">يتم التثبيت...</string>
|
||||
<string name="done">تم!</string>
|
||||
<string name="failure">فشل!</string>
|
||||
<string name="open_link_failed_toast">لم يُعثر على تطبيق لفتح الرابط …</string>
|
||||
<string name="complete_uninstall">إلغاء التثبيت بالكامل</string>
|
||||
<string name="restore_img">استعادة الصور</string>
|
||||
<string name="restore_img_msg">جار الأستعادة…</string>
|
||||
<string name="restore_done">تم الأستعادة</string>
|
||||
<string name="restore_fail">النسخة الاحتياطية الأصلية غير موجودة!</string>
|
||||
<string name="setup_fail">فشل التضبيط</string>
|
||||
<string name="env_fix_title"> الإعداد الأضافي مطلوب</string>
|
||||
<string name="setup_msg">جار تضبيت البيئة</string>
|
||||
<string name="unsupport_magisk_title">إصدار مـاجـيسك غير مدعوم</string>
|
||||
<string name="restore_img_msg">جار الإستعادة…</string>
|
||||
<string name="restore_done">تم الإستعادة</string>
|
||||
<string name="restore_fail">النسخة الإحتياطية الأصلية غير موجودة!</string>
|
||||
<string name="setup_fail">فشل الإعداد</string>
|
||||
<string name="env_fix_title">الإعداد الإضافي مطلوب</string>
|
||||
<string name="setup_msg">جار إعداد البيئة</string>
|
||||
<string name="unsupport_magisk_title">إصدار ماجيسك غير مدعوم</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,99 +1,57 @@
|
||||
<resources>
|
||||
<!--Universal-->
|
||||
|
||||
<!--Welcome Activity-->
|
||||
<!--Sections-->
|
||||
<string name="modules">Модули</string>
|
||||
<string name="superuser">Супер-корисник</string>
|
||||
<string name="logs">Дневник</string>
|
||||
<string name="logs">Логови</string>
|
||||
<string name="settings">Подешавања</string>
|
||||
<string name="install">Инсталација</string>
|
||||
<string name="section_home">Почетно</string>
|
||||
<string name="section_theme">Теме</string>
|
||||
<string name="denylist">Листа забрана</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="invalid_update_channel">Непостојећи Канал Ажурирања</string>
|
||||
<!--Home-->
|
||||
<string name="no_connection">Недоступна конекција</string>
|
||||
<string name="app_changelog">Промене у апликацији</string>
|
||||
<string name="loading">Учитавање…</string>
|
||||
<string name="update">Ажурирање</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Сакриј</string>
|
||||
<string name="home_package">Пакет</string>
|
||||
<string name="home_app_title">Апл.</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="home_notice_content">Преузмите Magisk САМО са званичне GitHub странице. Фајлови из непознатих извора могу бити малициозни!</string>
|
||||
<string name="home_support_title">Подржите нас</string>
|
||||
<string name="home_follow_title">Запратите нас</string>
|
||||
<string name="home_item_source">Извор</string>
|
||||
<string name="home_support_content">Magisk јесте и увек ће бити бесплатан и open source. Можете показати да вам је стало својом донацијом.</string>
|
||||
<string name="home_installed_version">Инсталирано</string>
|
||||
<string name="home_latest_version">Најновије</string>
|
||||
<string name="invalid_update_channel">Невалидан канал ажурирања</string>
|
||||
<string name="uninstall_magisk_title">Деинсталирај Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Сви модули ће бити онемогућени/уклоњени!\nКорен ће бити уклоњен!\nСвако неенкриптовано интерно складиште ће употребом Magisk-а бити поново енкриптовано!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Задржи форсирану енкрипцију</string>
|
||||
<string name="keep_dm_verity">Задржи AVB 2.0/dm-verity</string>
|
||||
<string name="uninstall_magisk_title">Унинсталирај Магиск</string>
|
||||
<string name="uninstall_magisk_msg">Сви модули ће бити онеспособљени/уклоњени. Корен ће бити уклоњен, и потенцијално енкриптовати твоје податке уколико већ нису енкриптовани</string>
|
||||
<string name="update">Ажурирање</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Без информација)</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Ажурирање доступно</string>
|
||||
<string name="home_installed_version">Инсталирано</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveLog">Сачувај дневник</string>
|
||||
<string name="menuClearLog">Обриши дневник</string>
|
||||
<string name="logs_cleared">Дневник успешно креиран</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<string name="app_changelog">Дневник промена апликације</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="repo_install_title">Инсталирај %1$s %2$s(%3$d)</string>
|
||||
<string name="download">Преузми</string>
|
||||
<string name="reboot">Рестартуј</string>
|
||||
<string name="magisk_update_title">Нови Адбејт Магиска Доступан!</string>
|
||||
<string name="release_notes">Белешке обљављивања</string>
|
||||
<string name="recovery_mode">Режим опоравка</string>
|
||||
<string name="install_options_title">Опције</string>
|
||||
<string name="install_method_title">Метод</string>
|
||||
<string name="install_next">Наредно</string>
|
||||
<string name="install_start">Почнимо</string>
|
||||
<string name="manager_download_install">Притисни да преузмеш и инсталираш</string>
|
||||
<string name="update_channel">Магиск Ажурирање</string>
|
||||
<string name="flashing">Флешовање</string>
|
||||
<string name="direct_install">Директна Инсталација (Препоручено)</string>
|
||||
<string name="complete_uninstall">Комплетна Унинсталација</string>
|
||||
<string name="restore_done">Повратак успешан!</string>
|
||||
<string name="restore_fail">Фабрички бекап не постоји!</string>
|
||||
<string name="download_file_error">Грешка при преузимању фајла</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="language">Језик</string>
|
||||
<string name="system_default">(Фабрички Система)</string>
|
||||
<string name="settings_update_channel_title">Канал Ажурирања</string>
|
||||
<string name="settings_update_stable">Стабилан</string>
|
||||
<string name="settings_update_beta">Бета</string>
|
||||
<string name="settings_update_custom">По наруџби</string>
|
||||
<string name="settings_update_custom_msg">Унеси наруџбени УРЛ</string>
|
||||
<string name="settings_hosts_title">Без-системски домаћини (hosts)</string>
|
||||
<string name="settings_hosts_summary">Подршка без-системских домаћина за апликације за блокирање реклама</string>
|
||||
|
||||
<string name="settings_su_app_adb">Апликације и АДБ</string>
|
||||
<string name="settings_su_app">Само Апликације</string>
|
||||
<string name="settings_su_adb">Само АДБ</string>
|
||||
<string name="settings_su_disable">Онемогућено</string>
|
||||
<string name="settings_su_request_10">10 секунди</string>
|
||||
<string name="settings_su_request_15">15 секунди</string>
|
||||
<string name="settings_su_request_20">20 секунди</string>
|
||||
<string name="settings_su_request_30">30 секунди</string>
|
||||
<string name="settings_su_request_45">45 секунди</string>
|
||||
<string name="settings_su_request_60">60 секунди</string>
|
||||
<string name="superuser_access">Приступ Супер-кориснику</string>
|
||||
<string name="auto_response">Аутоматски одговор</string>
|
||||
<string name="request_timeout">Истек захтева</string>
|
||||
<string name="superuser_notification">Нотификације Супер-корисника</string>
|
||||
<string name="settings_su_reauth_title">Поново одобри после ажурирања</string>
|
||||
<string name="settings_su_reauth_summary">Поново одобри дозволу Супер-корисника после ажурирања апликације</string>
|
||||
|
||||
<string name="multiuser_mode">Више-кориснички режим</string>
|
||||
<string name="settings_owner_only">Власник уређаја само</string>
|
||||
<string name="settings_owner_manage">Одређено од стране власника</string>
|
||||
<string name="settings_user_independent">Независно од корисника</string>
|
||||
<string name="owner_only_summary">Само власник има приступ корену</string>
|
||||
<string name="owner_manage_summary">Само власник може да приступа корену и да прими захтеве за њега</string>
|
||||
<string name="user_independent_summary">Сваки корисник има своја правила корена</string>
|
||||
|
||||
<string name="mount_namespace_mode">Постоље режима имена простора</string>
|
||||
<string name="settings_ns_global">Глобално име простора</string>
|
||||
<string name="settings_ns_requester">Наслеђено име простора</string>
|
||||
<string name="settings_ns_isolate">Ограђено име простора</string>
|
||||
<string name="global_summary">Све коренске сесије користе глобално име простора</string>
|
||||
<string name="requester_summary">Коренске сесије ће наследити свој простор именовања</string>
|
||||
<string name="isolate_summary">Свака коренска сесија ће имати свој изоловани простор именовања</string>
|
||||
<string name="direct_install">Директна инсталација (Препоручено)</string>
|
||||
<string name="install_inactive_slot">Инсталација на неактиван слот (Након OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Ваш уређај ће бити ФОРСИРАН да се покрене на тренутно неактивном слоту након поновног покретања!\nКористите опцију само кад се OTA заврши.\nНастави?</string>
|
||||
<string name="setup_title">Додатне поставке</string>
|
||||
<string name="select_patch_file">Изаберите фајл</string>
|
||||
<string name="patch_file_msg">Изаберите слику (*.img) или ODIN tarfile (*.tar) или payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">Поново покретање за 5 секунди…</string>
|
||||
<string name="flash_screen_title">Инсталација</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Супер-кориснички захтев</string>
|
||||
<string name="touch_filtered_warning">Magisk не може да верификује ваш одговор, јер апликација прикрива супер-кориснички захтев</string>
|
||||
<string name="deny">Забрани</string>
|
||||
<string name="prompt">Захтев</string>
|
||||
<string name="grant">Дозволи</string>
|
||||
@@ -104,20 +62,190 @@
|
||||
<string name="twentymin">20 мин</string>
|
||||
<string name="thirtymin">30 мин</string>
|
||||
<string name="sixtymin">60 мин</string>
|
||||
<string name="su_allow_toast">%1$s је добио права на Супер-корисника</string>
|
||||
<string name="su_deny_toast">%1$s није добио права на Супер-корисника</string>
|
||||
<string name="su_allow_toast">%1$s је добио права на супер-корисника</string>
|
||||
<string name="su_deny_toast">%1$s није добио права на супер-корисника</string>
|
||||
<string name="su_snack_grant">Супер-корисничка права од %1$s су пружена</string>
|
||||
<string name="su_snack_deny">Супер-корисничка права од %1$s су одбијена</string>
|
||||
<string name="su_snack_notif_on">Нотификације од %1$s су омогућене</string>
|
||||
<string name="su_snack_notif_off">Нотификације од %1$s су онемогућене</string>
|
||||
<string name="su_snack_log_on">Записивање у дневник за %1$s је омогућено</string>
|
||||
<string name="su_snack_log_off">Записивање у дневник за %1$s је онемогућено</string>
|
||||
<string name="su_snack_log_on">Логовање за %1$s је омогућено</string>
|
||||
<string name="su_snack_log_off">Логовање за %1$s је онемогућено</string>
|
||||
<string name="su_revoke_title">Опозови?</string>
|
||||
<string name="su_revoke_msg">Потврди да опозовеш права од %1$s?</string>
|
||||
<string name="toast">Тост</string>
|
||||
<string name="none">Ниједан</string>
|
||||
<string name="su_revoke_msg">Потврди да опозовеш права на супер-корисника од %1$s?</string>
|
||||
<string name="toast">Toast</string>
|
||||
<string name="none">Ништа</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="target_uid">Циљани УИД: %1$d</string>
|
||||
<string name="superuser_toggle_notification">Нотификације</string>
|
||||
<string name="superuser_toggle_revoke">Опозови</string>
|
||||
<string name="superuser_policy_none">Ниједна апликација није тражила пермисије за супер-корисника још увек.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Немате логова, покушајте користити коренске апликације више</string>
|
||||
<string name="log_data_magisk_none">Magisk логови су празни, то је чудно</string>
|
||||
<string name="menuSaveLog">Сачувај лог</string>
|
||||
<string name="menuClearLog">Уклони лог</string>
|
||||
<string name="logs_cleared">Лог успешно уклоњен</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Циљани UID: %1$d</string>
|
||||
<string name="target_pid">Mount ns цињани PID: %s</string>
|
||||
<string name="selinux_context">SELinux контекст: %s</string>
|
||||
<string name="supp_group">Допунска група: %s</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Прикажи системске апл.</string>
|
||||
<string name="show_os_app">Прикажи апл. ОС-а</string>
|
||||
<string name="hide_filter_hint">Филтрирај по имену</string>
|
||||
<string name="hide_search">Претрага</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Без информација)</string>
|
||||
<string name="reboot_userspace">Лако поново покретање</string>
|
||||
<string name="reboot_recovery">Поново покрени за опоравак</string>
|
||||
<string name="reboot_bootloader">Поново покрени за bootloader</string>
|
||||
<string name="reboot_download">Поново покрени за преузимање</string>
|
||||
<string name="reboot_edl">Поново покрени за EDL</string>
|
||||
<string name="reboot_safe_mode">Сигуран мод</string>
|
||||
<string name="module_version_author">%1$s од %2$s</string>
|
||||
<string name="module_state_remove">Уклони</string>
|
||||
<string name="module_action">Акција</string>
|
||||
<string name="module_state_restore">Поврати</string>
|
||||
<string name="module_action_install_external">Инсталирај из складишта</string>
|
||||
<string name="update_available">Ажурирање доступно</string>
|
||||
<string name="suspend_text_riru">Модул је суспендован јер је %1$s омогућено</string>
|
||||
<string name="suspend_text_zygisk">Модул је суспендован јер %1$s није омогућено</string>
|
||||
<string name="zygisk_module_unloaded">Zygisk модул није учитан због некомпатибилности</string>
|
||||
<string name="module_empty">Ниједан модул није инсталиран</string>
|
||||
<string name="confirm_install">Инсталирај модул %1$s?</string>
|
||||
<string name="confirm_install_title">Потврда инсталације</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Тема</string>
|
||||
<string name="settings_dark_mode_message">Изаберите тему која вам највише одговара!</string>
|
||||
<string name="settings_dark_mode_light">Увек светло</string>
|
||||
<string name="settings_dark_mode_system">Прати систем</string>
|
||||
<string name="settings_dark_mode_dark">Увек тамно</string>
|
||||
<string name="settings_download_path_title">Путања за преузимање</string>
|
||||
<string name="settings_download_path_message">Фајлови ће бити сачувани на %1$s</string>
|
||||
<string name="settings_hide_app_title">Сакриј Magisk апл.</string>
|
||||
<string name="settings_hide_app_summary">Инсталирај proxy апликацију са насумичним ID-јем пакета и прилагођеном лабелом</string>
|
||||
<string name="settings_restore_app_title">Поврати Magisk апл.</string>
|
||||
<string name="settings_restore_app_summary">Откриј апл. и поврати оригинални APK</string>
|
||||
<string name="language">Језик</string>
|
||||
<string name="system_default">(Подразумевано системски)</string>
|
||||
<string name="settings_check_update_title">Провери ажурирања</string>
|
||||
<string name="settings_check_update_summary">Периодично провери ажурирања у позадини</string>
|
||||
<string name="settings_update_channel_title">Канал ажурирања</string>
|
||||
<string name="settings_update_stable">Стабилно</string>
|
||||
<string name="settings_update_beta">Бета</string>
|
||||
<string name="settings_update_custom">Прилагођено</string>
|
||||
<string name="settings_update_custom_msg">Унеси прилагођени URL канала</string>
|
||||
<string name="settings_zygisk_summary">Покрени делове Magisk-а у zygote daemon-у</string>
|
||||
<string name="settings_denylist_title">Спроведи листу забрана</string>
|
||||
<string name="settings_denylist_summary">Процеси на листи забрана ће повратити све Magisk измене</string>
|
||||
<string name="settings_denylist_config_title">Конфигуриши листу забрана</string>
|
||||
<string name="settings_denylist_config_summary">Изабери процесе који ће бити на листи забрана</string>
|
||||
<string name="settings_hosts_title">Безсистемски домаћини (hosts)</string>
|
||||
<string name="settings_hosts_summary">Подршка безсистемских домаћина за апликације блокирања реклама</string>
|
||||
<string name="settings_hosts_toast">Модул безсистемских домаћина додат</string>
|
||||
<string name="settings_app_name_hint">Ново име</string>
|
||||
<string name="settings_app_name_helper">Апл. ће бити спакована под овим именом</string>
|
||||
<string name="settings_app_name_error">Невалидан формат</string>
|
||||
<string name="settings_su_app_adb">Апликације и ADB</string>
|
||||
<string name="settings_su_app">Само апликације</string>
|
||||
<string name="settings_su_adb">Само ADB</string>
|
||||
<string name="settings_su_disable">Онемогућено</string>
|
||||
<string name="settings_su_request_10">10 секунди</string>
|
||||
<string name="settings_su_request_15">15 секунди</string>
|
||||
<string name="settings_su_request_20">20 секунди</string>
|
||||
<string name="settings_su_request_30">30 секунди</string>
|
||||
<string name="settings_su_request_45">45 секунди</string>
|
||||
<string name="settings_su_request_60">60 секунди</string>
|
||||
<string name="superuser_access">Приступ супер-корисника</string>
|
||||
<string name="auto_response">Аутоматски одговор</string>
|
||||
<string name="request_timeout">Истек захтева</string>
|
||||
<string name="superuser_notification">Нотификације супер-корисника</string>
|
||||
<string name="settings_su_reauth_title">Поново одобри након ажурирања</string>
|
||||
<string name="settings_su_reauth_summary">Поново тражи пермисије супер-корисника након ажурирања апликација</string>
|
||||
<string name="settings_su_tapjack_title">Заштита од tapjacking-а</string>
|
||||
<string name="settings_su_tapjack_summary">Prompt дијалог супер-корисника неће реаговати док је прикривен другим прозором или overlay-ем</string>
|
||||
<string name="settings_su_auth_title">Аутентификација корисника</string>
|
||||
<string name="settings_su_auth_summary">Тражи аутентификацију корисника током захтева супер-корисника</string>
|
||||
<string name="settings_su_auth_insecure">Ниједан метод аутентификације није подешен на уређају</string>
|
||||
<string name="settings_customization">Прилагођавање</string>
|
||||
<string name="setting_add_shortcut_summary">Додај лепу пречицу на почетни екран у случају да се име и иконица не препознају лако након скривања апликације</string>
|
||||
<string name="settings_doh_title">DNS преко HTTPS-а</string>
|
||||
<string name="settings_doh_description">Заобилазно решење DNS тровања у неким нацијама</string>
|
||||
<string name="settings_random_name_title">Насумично име на излазу</string>
|
||||
<string name="settings_random_name_description">Насумично име излазног фајла слика и tar фајлова ради спречавања детекције</string>
|
||||
|
||||
<string name="multiuser_mode">Вишекориснички режим</string>
|
||||
<string name="settings_owner_only">Само власник уређаја</string>
|
||||
<string name="settings_owner_manage">Одређено од стране власника</string>
|
||||
<string name="settings_user_independent">Независно од корисника</string>
|
||||
<string name="owner_only_summary">Само власник има приступ корену</string>
|
||||
<string name="owner_manage_summary">Само власник може да приступа корену и да прима захтеве за њега</string>
|
||||
<string name="user_independent_summary">Сваки корисник има своја правила корена</string>
|
||||
|
||||
<string name="mount_namespace_mode">Mount режим namespace-а</string>
|
||||
<string name="settings_ns_global">Глобални namespace</string>
|
||||
<string name="settings_ns_requester">Наслеђени namespace</string>
|
||||
<string name="settings_ns_isolate">Изоловани namespace</string>
|
||||
<string name="global_summary">Све коренске сесије користе глобални mount namespace</string>
|
||||
<string name="requester_summary">Коренске сесије ће наследити namespace од подносиоца захтева</string>
|
||||
<string name="isolate_summary">Свака коренска сесија ће имати свој изоловани namespace</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Ажурирања Magisk-а</string>
|
||||
<string name="progress_channel">Нотификације о прогресу</string>
|
||||
<string name="updated_channel">Ажурирање завршено</string>
|
||||
<string name="download_complete">Преузимање завршено</string>
|
||||
<string name="download_file_error">Грешка при преузимању фајла</string>
|
||||
<string name="magisk_update_title">Ажурирање Magisk-а доступно!</string>
|
||||
<string name="updated_title">Magisk је ажуриран</string>
|
||||
<string name="updated_text">Кликни да отвориш апликацију</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Да</string>
|
||||
<string name="no">Не</string>
|
||||
<string name="repo_install_title">Инсталирај %1$s %2$s(%3$d)</string>
|
||||
<string name="download">Преузми</string>
|
||||
<string name="reboot">Поново покрени</string>
|
||||
<string name="close">Затвори</string>
|
||||
<string name="release_notes">Release notes</string>
|
||||
<string name="flashing">Флешовање…</string>
|
||||
<string name="running">Покретање…</string>
|
||||
<string name="done">Завршено!</string>
|
||||
<string name="done_action">Покретање акције %1$s завршено</string>
|
||||
<string name="failure">Неуспешно!</string>
|
||||
<string name="hide_app_title">Скривање Magisk апликације…</string>
|
||||
<string name="open_link_failed_toast">Није пронађена апликација за отварање линка</string>
|
||||
<string name="complete_uninstall">Комплетна деинсталација</string>
|
||||
<string name="restore_img">Поврати слике</string>
|
||||
<string name="restore_img_msg">Повратак…</string>
|
||||
<string name="restore_done">Повратак успешан!</string>
|
||||
<string name="restore_fail">Фабрички бекап не постоји!</string>
|
||||
<string name="setup_fail">Неуспешна поставка</string>
|
||||
<string name="env_fix_title">Потребно додатно подешавање</string>
|
||||
<string name="env_fix_msg">Ваш уређај захтева додатно подешавање да би Magisk радио како треба. Да ли желите наставити и покренути поново?</string>
|
||||
<string name="env_full_fix_msg">Ваш уређај захтева поновно флешовање да би Magisk радио како треба. Реинсталирајте Magisk кроз апликацију, режим опоравка не може добити тачне информације о уређају.</string>
|
||||
<string name="setup_msg">Покретање подешавања окружења…</string>
|
||||
<string name="unsupport_magisk_title">Неподржана верзија Magisk-а</string>
|
||||
<string name="unsupport_magisk_msg">Ова верзија апликације не подржава Magisk верзије мање од %1$s.\n\nАпликација ће се понашати као да Magisk није инсталиран, молимо ажурирајте Magisk што пре.</string>
|
||||
<string name="unsupport_general_title">Ненормално стање</string>
|
||||
<string name="unsupport_system_app_msg">Покретање апликације као системске није подржано. Молимо поставите апликацију да буде корисничка.</string>
|
||||
<string name="unsupport_other_su_msg">Детектован \"su\" binary који није Magisk-ов. Молимо уклоните конкурентно коренско решење и/или реинсталирајте Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk је инсталиран на екстерно складиште. Молимо померите апл. у интерно складиште.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Скривена Magisk апликација не може наставити са радом јер је корен изгубљен. Молимо повратите оригинални APK.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Дозволите пермисију за складиште да бисте омогућили ову функционалност</string>
|
||||
<string name="post_notifications_denied">Дозволите пермисију за нотификације да бисте омогућили ову функционалност</string>
|
||||
<string name="install_unknown_denied">Дозволите "инсталирање непознатих апликација" да бисте омогућили ову функционалност</string>
|
||||
<string name="add_shortcut_title">Додај пречицу на почетни екран</string>
|
||||
<string name="add_shortcut_msg">Након скривања апликације, њено име и иконицу ћете тешко препознати. Желите ли додати лепу пречицу на почетни екран?</string>
|
||||
<string name="app_not_found">Није пронађена апликација за ову акцију</string>
|
||||
<string name="reboot_apply_change">Поново покрени да примениш измене</string>
|
||||
<string name="restore_app_confirmation">Ово ће вратити скривену апликацију на оригиналну. Да ли стварно то желите?</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -30,5 +30,4 @@ android.nonFinalResIds=false
|
||||
|
||||
# Magisk
|
||||
magisk.stubVersion=40
|
||||
magisk.versionCode=28103
|
||||
magisk.ondkVersion=r29.0
|
||||
magisk.versionCode=29001
|
||||
@@ -1,14 +1,14 @@
|
||||
[versions]
|
||||
kotlin = "2.1.10"
|
||||
android = "8.9.0"
|
||||
ksp = "2.1.10-1.0.31"
|
||||
kotlin = "2.1.20"
|
||||
android = "8.9.2"
|
||||
ksp = "2.1.20-1.0.31"
|
||||
rikka = "1.3.0"
|
||||
navigation = "2.8.4"
|
||||
navigation = "2.8.9"
|
||||
libsu = "6.0.0"
|
||||
moshi = "1.15.1"
|
||||
moshi = "1.15.2"
|
||||
okhttp = "4.12.0"
|
||||
retrofit = "2.11.0"
|
||||
room = "2.6.1"
|
||||
room = "2.7.1"
|
||||
|
||||
[libraries]
|
||||
bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version = "1.80" }
|
||||
@@ -26,12 +26,12 @@ timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" }
|
||||
jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version = "7.1.0.202411261347-r" }
|
||||
|
||||
# AndroidX
|
||||
activity = { module = "androidx.activity:activity", version = "1.10.0" }
|
||||
activity = { module = "androidx.activity:activity", version = "1.10.1" }
|
||||
appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.0" }
|
||||
core-ktx = { module = "androidx.core:core-ktx", version = "1.15.0" }
|
||||
core-ktx = { module = "androidx.core:core-ktx", version = "1.16.0" }
|
||||
core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.0.1" }
|
||||
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.0" }
|
||||
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.5" }
|
||||
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.1" }
|
||||
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.6" }
|
||||
navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigation" }
|
||||
navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigation" }
|
||||
profileinstaller = { module = "androidx.profileinstaller:profileinstaller", version = "1.4.1" }
|
||||
@@ -40,10 +40,10 @@ room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version = "1.1.0" }
|
||||
transition = { module = "androidx.transition:transition", version = "1.5.1" }
|
||||
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.4.5" }
|
||||
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.3" }
|
||||
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" }
|
||||
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
0
gradlew → app/gradlew
vendored
0
gradlew → app/gradlew
vendored
0
gradlew.bat → app/gradlew.bat
vendored
0
gradlew.bat → app/gradlew.bat
vendored
@@ -8,4 +8,4 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
rootProject.name = "Magisk"
|
||||
include(":app:apk", ":app:core", ":app:shared", ":app:stub", ":app:test", ":native")
|
||||
include(":apk", ":core", ":shared", ":stub", ":test")
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:enableOnBackInvokedCallback="false"
|
||||
android:label="Magisk"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:supportsRtl="true"
|
||||
|
||||
@@ -41,5 +41,5 @@ android {
|
||||
setupStubApk()
|
||||
|
||||
dependencies {
|
||||
implementation(project(":app:shared"))
|
||||
implementation(project(":shared"))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">عليك الترقية Magisk لإكمال تهيئة التطبيق. هل تريد التنزيل والتثبيت؟</string>
|
||||
<string name="no_internet_msg">يرجى اللإتصال بالإنترنت! ترقية Magisk مطلوبة.</string>
|
||||
<string name="upgrade_msg">عليك بتحديث ماجيسك لإكمال تهيئة التطبيق. هل تريد التنزيل والتثبيت؟</string>
|
||||
<string name="no_internet_msg">يرجى الإتصال بالإنترنت! تحديث ماجيسك مطلوب.</string>
|
||||
<string name="dling">جارٍ التنزيل</string>
|
||||
<string name="relaunch_app">يرجى إعادة تشغيل التطبيق يدوياً</string>
|
||||
</resources>
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
<resources></resources>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Ажурирајте Magisk да бисте завршили постављање. Преузми и инсталирај?</string>
|
||||
<string name="no_internet_msg">Молимо повежите се на интернет! Неопходно је ажурирање Magisk-а.</string>
|
||||
<string name="dling">Преузимање</string>
|
||||
<string name="relaunch_app">Молимо покрените апликацију поново</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
plugins {
|
||||
id("MagiskPlugin")
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete::class) {
|
||||
delete(rootProject.layout.buildDirectory)
|
||||
}
|
||||
152
build.py
152
build.py
@@ -67,9 +67,11 @@ support_abis = {
|
||||
"x86_64": "x86_64-linux-android",
|
||||
"riscv64": "riscv64-linux-android",
|
||||
}
|
||||
default_archs = {"armeabi-v7a", "x86", "arm64-v8a", "x86_64"}
|
||||
default_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
|
||||
support_targets = default_targets | {"resetprop"}
|
||||
rust_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
|
||||
ondk_version = "r29.1"
|
||||
|
||||
# Global vars
|
||||
config = {}
|
||||
@@ -148,27 +150,25 @@ def cmd_out(cmds: list):
|
||||
|
||||
|
||||
def clean_elf():
|
||||
if is_windows:
|
||||
elf_cleaner = Path("tools", "elf-cleaner.exe")
|
||||
else:
|
||||
elf_cleaner = Path("native", "out", "elf-cleaner")
|
||||
if not elf_cleaner.exists():
|
||||
execv(
|
||||
[
|
||||
"gcc",
|
||||
'-DPACKAGE_NAME="termux-elf-cleaner"',
|
||||
'-DPACKAGE_VERSION="2.1.1"',
|
||||
'-DCOPYRIGHT="Copyright (C) 2022 Termux."',
|
||||
"tools/termux-elf-cleaner/elf-cleaner.cpp",
|
||||
"tools/termux-elf-cleaner/arghandling.c",
|
||||
"-o",
|
||||
elf_cleaner,
|
||||
]
|
||||
)
|
||||
cmds = [elf_cleaner, "--api-level", "23"]
|
||||
cargo_toml = Path("tools", "elf-cleaner", "Cargo.toml")
|
||||
cmds = ["run", "--release", "--manifest-path", cargo_toml]
|
||||
if args.verbose == 0:
|
||||
cmds.append("-q")
|
||||
elif args.verbose > 1:
|
||||
cmds.append("--verbose")
|
||||
cmds.append("--")
|
||||
cmds.extend(glob.glob("native/out/*/magisk"))
|
||||
cmds.extend(glob.glob("native/out/*/magiskpolicy"))
|
||||
execv(cmds)
|
||||
run_cargo(cmds)
|
||||
|
||||
|
||||
def collect_ndk_build():
|
||||
for arch in build_abis.keys():
|
||||
arch_dir = Path("native", "libs", arch)
|
||||
out_dir = Path("native", "out", arch)
|
||||
for source in arch_dir.iterdir():
|
||||
target = out_dir / source.name
|
||||
mv(source, target)
|
||||
|
||||
|
||||
def run_ndk_build(cmds: list):
|
||||
@@ -186,13 +186,6 @@ def run_ndk_build(cmds: list):
|
||||
error("Build binary failed!")
|
||||
os.chdir("..")
|
||||
|
||||
for arch in build_abis.keys():
|
||||
arch_dir = Path("native", "libs", arch)
|
||||
out_dir = Path("native", "out", arch)
|
||||
for source in arch_dir.iterdir():
|
||||
target = out_dir / source.name
|
||||
mv(source, target)
|
||||
|
||||
|
||||
def build_cpp_src(targets: set):
|
||||
cmds = []
|
||||
@@ -214,6 +207,7 @@ def build_cpp_src(targets: set):
|
||||
|
||||
if cmds:
|
||||
run_ndk_build(cmds)
|
||||
collect_ndk_build()
|
||||
|
||||
cmds.clear()
|
||||
|
||||
@@ -226,6 +220,7 @@ def build_cpp_src(targets: set):
|
||||
if cmds:
|
||||
cmds.append("B_CRT0=1")
|
||||
run_ndk_build(cmds)
|
||||
collect_ndk_build()
|
||||
|
||||
if clean:
|
||||
clean_elf()
|
||||
@@ -234,10 +229,9 @@ def build_cpp_src(targets: set):
|
||||
def run_cargo(cmds):
|
||||
ensure_paths()
|
||||
env = os.environ.copy()
|
||||
env["PATH"] = f'{rust_bin}{os.pathsep}{env["PATH"]}'
|
||||
env["CARGO_BUILD_RUSTC"] = str(rust_bin / f"rustc{EXE_EXT}")
|
||||
env["RUSTUP_TOOLCHAIN"] = str(rust_sysroot)
|
||||
env["CARGO_BUILD_RUSTFLAGS"] = f"-Z threads={min(8, cpu_count)}"
|
||||
return execv([cargo, *cmds], env)
|
||||
return execv(["cargo", *cmds], env)
|
||||
|
||||
|
||||
def build_rust_src(targets: set):
|
||||
@@ -325,7 +319,7 @@ def build_native():
|
||||
# Verify NDK install
|
||||
try:
|
||||
with open(Path(ndk_path, "ONDK_VERSION"), "r") as ondk_ver:
|
||||
assert ondk_ver.read().strip(" \t\r\n") == config["ondkVersion"]
|
||||
assert ondk_ver.read().strip(" \t\r\n") == ondk_version
|
||||
except:
|
||||
error('Unmatched NDK. Please install/upgrade NDK with "build.py ndk"')
|
||||
|
||||
@@ -390,16 +384,19 @@ def find_jdk():
|
||||
def build_apk(module: str):
|
||||
ensure_paths()
|
||||
env = find_jdk()
|
||||
props = args.config.resolve()
|
||||
|
||||
os.chdir("app")
|
||||
build_type = "Release" if args.release else "Debug"
|
||||
proc = execv(
|
||||
[
|
||||
gradlew,
|
||||
f"{module}:assemble{build_type}",
|
||||
f"-PconfigPath={args.config.resolve()}",
|
||||
f"-PconfigPath={props}",
|
||||
],
|
||||
env=env,
|
||||
)
|
||||
os.chdir("..")
|
||||
if proc.returncode != 0:
|
||||
error(f"Build {module} failed!")
|
||||
|
||||
@@ -408,7 +405,7 @@ def build_apk(module: str):
|
||||
paths = module.split(":")
|
||||
|
||||
apk = f"{paths[-1]}-{build_type}.apk"
|
||||
source = Path(*paths, "build", "outputs", "apk", build_type, apk)
|
||||
source = Path("app", *paths, "build", "outputs", "apk", build_type, apk)
|
||||
target = config["outdir"] / apk
|
||||
mv(source, target)
|
||||
return target
|
||||
@@ -416,7 +413,7 @@ def build_apk(module: str):
|
||||
|
||||
def build_app():
|
||||
header("* Building the Magisk app")
|
||||
apk = build_apk(":app:apk")
|
||||
apk = build_apk(":apk")
|
||||
|
||||
build_type = "release" if args.release else "debug"
|
||||
|
||||
@@ -435,7 +432,7 @@ def build_app():
|
||||
|
||||
def build_stub():
|
||||
header("* Building the stub app")
|
||||
apk = build_apk(":app:stub")
|
||||
apk = build_apk(":stub")
|
||||
header(f"Output: {apk}")
|
||||
|
||||
|
||||
@@ -446,7 +443,7 @@ def build_test():
|
||||
args.release = True
|
||||
try:
|
||||
header("* Building the test app")
|
||||
source = build_apk(":app:test")
|
||||
source = build_apk(":test")
|
||||
target = source.parent / "test.apk"
|
||||
mv(source, target)
|
||||
header(f"Output: {target}")
|
||||
@@ -484,11 +481,15 @@ def cleanup():
|
||||
rm(rs_gen)
|
||||
|
||||
if "native" in targets:
|
||||
header("* Cleaning native")
|
||||
rm_rf(Path("native", "out"))
|
||||
rm_rf(Path("tools", "elf-cleaner", "target"))
|
||||
|
||||
if "app" in targets:
|
||||
header("* Cleaning app")
|
||||
execv([gradlew, ":app:clean"], env=find_jdk())
|
||||
os.chdir("app")
|
||||
execv([gradlew, ":clean"], env=find_jdk())
|
||||
os.chdir("..")
|
||||
|
||||
|
||||
def build_all():
|
||||
@@ -502,14 +503,43 @@ def build_all():
|
||||
############
|
||||
|
||||
|
||||
def gen_ide():
|
||||
ensure_paths()
|
||||
set_archs({args.abi})
|
||||
|
||||
# Dump flags for both C++ and Rust code
|
||||
dump_flag_header()
|
||||
|
||||
# Run build.rs to generate Rust/C++ FFI bindings
|
||||
os.chdir(Path("native", "src"))
|
||||
run_cargo(["check"])
|
||||
os.chdir(Path("..", ".."))
|
||||
|
||||
# Generate compilation database
|
||||
rm_rf(Path("native", "compile_commands.json"))
|
||||
run_ndk_build(
|
||||
[
|
||||
"B_MAGISK=1",
|
||||
"B_INIT=1",
|
||||
"B_BOOT=1",
|
||||
"B_POLICY=1",
|
||||
"B_PRELOAD=1",
|
||||
"B_PROP=1",
|
||||
"B_CRT0=1",
|
||||
"compile_commands.json",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def clippy_cli():
|
||||
args.force_out = True
|
||||
set_archs(default_archs)
|
||||
|
||||
os.chdir(Path("native", "src"))
|
||||
cmds = ["clippy", "--no-deps", "--target", ""]
|
||||
cmds = ["clippy", "--no-deps", "--target"]
|
||||
for triple in build_abis.values():
|
||||
cmds[3] = triple
|
||||
run_cargo(cmds)
|
||||
run_cargo(cmds + ["--release"])
|
||||
run_cargo(cmds + [triple])
|
||||
run_cargo(cmds + [triple, "--release"])
|
||||
os.chdir(Path("..", ".."))
|
||||
|
||||
|
||||
@@ -524,10 +554,9 @@ def cargo_cli():
|
||||
|
||||
def setup_ndk():
|
||||
ensure_paths()
|
||||
ndk_ver = config["ondkVersion"]
|
||||
url = f"https://github.com/topjohnwu/ondk/releases/download/{ndk_ver}/ondk-{ndk_ver}-{os_name}.tar.xz"
|
||||
url = f"https://github.com/topjohnwu/ondk/releases/download/{ondk_version}/ondk-{ondk_version}-{os_name}.tar.xz"
|
||||
ndk_archive = url.split("/")[-1]
|
||||
ondk_path = Path(ndk_root, f"ondk-{ndk_ver}")
|
||||
ondk_path = Path(ndk_root, f"ondk-{ondk_version}")
|
||||
|
||||
header(f"* Downloading and extracting {ndk_archive}")
|
||||
rm_rf(ondk_path)
|
||||
@@ -555,8 +584,8 @@ def setup_rustup():
|
||||
tgt = wrapper_dir / src.name
|
||||
tgt.symlink_to(f"rustup{EXE_EXT}")
|
||||
|
||||
# Build rustup_wrapper
|
||||
wrapper_src = Path("tools", "rustup_wrapper")
|
||||
# Build rustup-wrapper
|
||||
wrapper_src = Path("tools", "rustup-wrapper")
|
||||
cargo_toml = wrapper_src / "Cargo.toml"
|
||||
cmds = ["build", "--release", f"--manifest-path={cargo_toml}"]
|
||||
if args.verbose > 1:
|
||||
@@ -566,7 +595,7 @@ def setup_rustup():
|
||||
# Replace rustup with wrapper
|
||||
wrapper = wrapper_dir / (f"rustup{EXE_EXT}")
|
||||
wrapper.unlink(missing_ok=True)
|
||||
cp(wrapper_src / "target" / "release" / (f"rustup_wrapper{EXE_EXT}"), wrapper)
|
||||
cp(wrapper_src / "target" / "release" / (f"rustup-wrapper{EXE_EXT}"), wrapper)
|
||||
wrapper.chmod(0o755)
|
||||
|
||||
|
||||
@@ -652,8 +681,8 @@ def patch_avd_file():
|
||||
|
||||
|
||||
def ensure_paths():
|
||||
global sdk_path, ndk_root, ndk_path, ndk_build, rust_bin
|
||||
global llvm_bin, cargo, gradlew, adb_path, native_gen_path
|
||||
global sdk_path, ndk_root, ndk_path, ndk_build, rust_sysroot
|
||||
global llvm_bin, gradlew, adb_path, native_gen_path
|
||||
|
||||
# Skip if already initialized
|
||||
if "sdk_path" in globals():
|
||||
@@ -670,13 +699,12 @@ def ensure_paths():
|
||||
ndk_root = sdk_path / "ndk"
|
||||
ndk_path = ndk_root / "magisk"
|
||||
ndk_build = ndk_path / "ndk-build"
|
||||
rust_bin = ndk_path / "toolchains" / "rust" / "bin"
|
||||
rust_sysroot = ndk_path / "toolchains" / "rust"
|
||||
llvm_bin = (
|
||||
ndk_path / "toolchains" / "llvm" / "prebuilt" / f"{os_name}-x86_64" / "bin"
|
||||
)
|
||||
cargo = rust_bin / "cargo"
|
||||
adb_path = sdk_path / "platform-tools" / "adb"
|
||||
gradlew = Path.cwd() / "gradlew"
|
||||
gradlew = Path.cwd() / "app" / "gradlew"
|
||||
|
||||
|
||||
# We allow using several functionality with only ADB
|
||||
@@ -707,6 +735,12 @@ def parse_props(file):
|
||||
return props
|
||||
|
||||
|
||||
def set_archs(archs: set):
|
||||
triples = map(support_abis.get, archs)
|
||||
global build_abis
|
||||
build_abis = dict(zip(archs, triples))
|
||||
|
||||
|
||||
def load_config():
|
||||
commit_hash = cmd_out(["git", "rev-parse", "--short=8", "HEAD"])
|
||||
|
||||
@@ -721,8 +755,9 @@ def load_config():
|
||||
if args.config.exists():
|
||||
config.update(parse_props(args.config))
|
||||
|
||||
if Path("gradle.properties").exists():
|
||||
for key, value in parse_props("gradle.properties").items():
|
||||
gradle_props = Path("app", "gradle.properties")
|
||||
if gradle_props.exists():
|
||||
for key, value in parse_props(gradle_props).items():
|
||||
if key.startswith("magisk."):
|
||||
config[key[7:]] = value
|
||||
|
||||
@@ -738,12 +773,9 @@ def load_config():
|
||||
abiList = re.split("\\s*,\\s*", config["abiList"])
|
||||
archs = set(abiList) & support_abis.keys()
|
||||
else:
|
||||
archs = {"armeabi-v7a", "x86", "arm64-v8a", "x86_64"}
|
||||
archs = default_archs
|
||||
|
||||
triples = map(support_abis.get, archs)
|
||||
|
||||
global build_abis
|
||||
build_abis = dict(zip(archs, triples))
|
||||
set_archs(archs)
|
||||
|
||||
|
||||
def parse_args():
|
||||
@@ -814,12 +846,16 @@ def parse_args():
|
||||
"wrapper_dir", help="path to setup rustup wrapper binaries"
|
||||
)
|
||||
|
||||
gen_parser = subparsers.add_parser("gen", help="generate files for IDE")
|
||||
gen_parser.add_argument("--abi", default="arm64-v8a", help="target ABI to generate")
|
||||
|
||||
# Set callbacks
|
||||
all_parser.set_defaults(func=build_all)
|
||||
native_parser.set_defaults(func=build_native)
|
||||
cargo_parser.set_defaults(func=cargo_cli)
|
||||
clippy_parser.set_defaults(func=clippy_cli)
|
||||
rustup_parser.set_defaults(func=setup_rustup)
|
||||
gen_parser.set_defaults(func=gen_ide)
|
||||
app_parser.set_defaults(func=build_app)
|
||||
stub_parser.set_defaults(func=build_stub)
|
||||
test_parser.set_defaults(func=build_test)
|
||||
|
||||
@@ -40,12 +40,9 @@
|
||||
|
||||
### Developing Rust
|
||||
|
||||
The Magisk NDK package [ONDK](https://github.com/topjohnwu/ondk) (the one installed with `./build.py ndk`) bundles a complete Rust toolchain, so _building_ the Magisk project itself does not require any further configuration. However, if you'd like to work on the Rust codebase with proper support, you'd need some setup as most development tools are built around `rustup`.
|
||||
First, install [rustup](https://www.rust-lang.org/tools/install), the official Rust toolchain manager. The Magisk NDK package [ONDK](https://github.com/topjohnwu/ondk) (the one installed with `./build.py ndk`) bundles a complete Rust toolchain, so _building_ the Magisk project itself does not require any further configuration.
|
||||
|
||||
Let's first setup `rustup` to use our custom ONDK Rust toolchain by default:
|
||||
|
||||
- Install [rustup](https://rustup.rs/), the official Rust toolchain manager
|
||||
- Link the ONDK Rust toolchain and set it as default:
|
||||
However, if you'd like to work on the Rust codebase, it'll be easier if you link ONDK's Rust toolchain in `rustup` and set it as default so several development tools and IDEs will work properly:
|
||||
|
||||
```bash
|
||||
# Link the ONDK toolchain with the name "magisk"
|
||||
@@ -54,7 +51,7 @@ rustup toolchain link magisk "$ANDROID_HOME/ndk/magisk/toolchains/rust"
|
||||
rustup default magisk
|
||||
```
|
||||
|
||||
If you plan to use VSCode, you can then install the [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) plugin and everything should be good to go. If you plan to use Jetbrain IDEs (e.g. [Rustrover](https://www.jetbrains.com/rust/), or its Rust Plugin), due to its poor support with custom toolchains, we need some additional setup:
|
||||
If you plan to use VSCode, you can then install the [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) plugin and everything should be good to go. If you plan to use Jetbrain IDEs (e.g. [Rustrover](https://www.jetbrains.com/rust/), or its Rust Plugin), we need some additional setup:
|
||||
|
||||
- Install the official nightly toolchain and add some components. We won't actually use the nightly toolchain for anything other than tricking the IDE to cooperate; the magic happens in the wrapper we setup in the next step.
|
||||
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# Magisk Changelog
|
||||
|
||||
### v29.0
|
||||
|
||||
- [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
|
||||
|
||||
### v28.1
|
||||
|
||||
- [App] Fix stub APK download link
|
||||
|
||||
@@ -76,7 +76,7 @@ As a summary, after installing Magisk in recovery **(starting from power off)**:
|
||||
Before proceeding, please acknowledge that:
|
||||
|
||||
- Installing Magisk **WILL** trip your Knox Warranty Bit, this action is not reversible in any way.
|
||||
- Installing Magisk for the first time **REQUIRES** a full data wipe (this is **NOT** counting the data wipe when unlocking bootloader). Please make a backup your data.
|
||||
- Installing Magisk for the first time **REQUIRES** a full data wipe (this is **NOT** counting the data wipe when unlocking bootloader). Please make a backup of your data.
|
||||
|
||||
### Flashing Tools
|
||||
|
||||
@@ -86,7 +86,7 @@ Before proceeding, please acknowledge that:
|
||||
|
||||
### Requirements
|
||||
|
||||
To verify whether or not Magisk can be installed in your Samsung device, you first must check the OEM Lock and KnoxGuard (RMM) status, to do so boot your device in Download mode with its key combo.
|
||||
To verify whether or not Magisk can be installed in your Samsung device, you first must check the OEM Lock and KnoxGuard (RMM) status. To do so, boot your device in Download mode with its key combo.
|
||||
|
||||
Possible OEM Lock values are the following:
|
||||
- **ON (L)**: fully locked.
|
||||
|
||||
16
docs/releases/29000.md
Normal file
16
docs/releases/29000.md
Normal file
@@ -0,0 +1,16 @@
|
||||
## 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)
|
||||
@@ -1,5 +1,6 @@
|
||||
# Release Notes
|
||||
|
||||
- [v29.0](29000.md)
|
||||
- [v28.1](28100.md)
|
||||
- [v28.0](28000.md)
|
||||
- [v27.0](27000.md)
|
||||
|
||||
1
native/.gitignore
vendored
1
native/.gitignore
vendored
@@ -5,3 +5,4 @@ libs
|
||||
/.cxx
|
||||
*-rs.cpp
|
||||
*-rs.hpp
|
||||
/compile_commands.json
|
||||
|
||||
@@ -19,7 +19,6 @@ LOCAL_SRC_FILES := \
|
||||
core/magisk.cpp \
|
||||
core/daemon.cpp \
|
||||
core/scripting.cpp \
|
||||
core/selinux.cpp \
|
||||
core/sqlite.cpp \
|
||||
core/module.cpp \
|
||||
core/thread.cpp \
|
||||
@@ -27,7 +26,6 @@ LOCAL_SRC_FILES := \
|
||||
core/resetprop/resetprop.cpp \
|
||||
core/su/su.cpp \
|
||||
core/su/connect.cpp \
|
||||
core/su/pts.cpp \
|
||||
core/zygisk/entry.cpp \
|
||||
core/zygisk/module.cpp \
|
||||
core/zygisk/hook.cpp \
|
||||
@@ -66,7 +64,6 @@ LOCAL_SRC_FILES := \
|
||||
init/mount.cpp \
|
||||
init/rootdir.cpp \
|
||||
init/getinfo.cpp \
|
||||
init/selinux.cpp \
|
||||
init/init-rs.cpp
|
||||
|
||||
LOCAL_LDFLAGS := -static
|
||||
|
||||
@@ -5,7 +5,6 @@ APP_STL := none
|
||||
APP_PLATFORM := android-23
|
||||
APP_THIN_ARCHIVE := true
|
||||
APP_STRIP_MODE := none
|
||||
APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true
|
||||
|
||||
ifdef MAGISK_DEBUG
|
||||
|
||||
|
||||
76
native/src/Cargo.lock
generated
76
native/src/Cargo.lock
generated
@@ -67,9 +67,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
version = "1.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
@@ -106,9 +106,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck_derive"
|
||||
version = "1.8.1"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a"
|
||||
checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -123,9 +123,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.16"
|
||||
version = "1.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@@ -138,18 +138,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.31"
|
||||
version = "4.5.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
|
||||
checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.31"
|
||||
version = "4.5.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
|
||||
checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
@@ -174,9 +174,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cb3c4a0d3776f7535c32793be81d6d5fec0d48ac70955d9834e643aa249a52f"
|
||||
checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e"
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
@@ -234,9 +234,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crypto-primes"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76db6c35962f23f58ab08c156831c55caf69fe85a63ae3780d4fdb24c38b32b6"
|
||||
checksum = "2acbaf157961745008b5a80ee1cc974150691304fe9177edf69747142bfd9878"
|
||||
dependencies = [
|
||||
"crypto-bigint",
|
||||
"rand_core",
|
||||
@@ -376,9 +376,9 @@ checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.0"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
|
||||
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
"subtle",
|
||||
@@ -386,15 +386,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flagset"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
|
||||
checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
@@ -457,24 +457,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.170"
|
||||
version = "0.2.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
|
||||
[[package]]
|
||||
name = "libz-rs-sys"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "902bc563b5d65ad9bba616b490842ef0651066a1a1dc3ce1087113ffcb873c8d"
|
||||
checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a"
|
||||
dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.26"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "magisk"
|
||||
@@ -627,7 +627,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pb-rs"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/tafia/quick-protobuf.git?rev=2f37d5a65504de7d716b5b28fd82219501a901a9#2f37d5a65504de7d716b5b28fd82219501a901a9"
|
||||
source = "git+https://github.com/tafia/quick-protobuf.git#54e7d6c5d981c6f7cec2e9a2167c10ed0f9392b4"
|
||||
dependencies = [
|
||||
"log",
|
||||
"nom",
|
||||
@@ -690,16 +690,16 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "quick-protobuf"
|
||||
version = "0.8.1"
|
||||
source = "git+https://github.com/tafia/quick-protobuf.git?rev=2f37d5a65504de7d716b5b28fd82219501a901a9#2f37d5a65504de7d716b5b28fd82219501a901a9"
|
||||
source = "git+https://github.com/tafia/quick-protobuf.git#54e7d6c5d981c6f7cec2e9a2167c10ed0f9392b4"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.39"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -771,18 +771,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.218"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.218"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -867,9 +867,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.99"
|
||||
version = "2.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1072,6 +1072,6 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b20717f0917c908dc63de2e44e97f1e6b126ca58d0e391cee86d504eb8fbd05"
|
||||
checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8"
|
||||
|
||||
@@ -10,45 +10,44 @@ edition = "2024"
|
||||
[workspace.dependencies]
|
||||
cxx = { path = "external/cxx-rs" }
|
||||
cxx-gen = { path = "external/cxx-rs/gen/lib" }
|
||||
libc = "0.2"
|
||||
cfg-if = "1.0"
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.4"
|
||||
thiserror = "2.0"
|
||||
byteorder = "1"
|
||||
size = "0.5"
|
||||
sha1 = "0.11.0-pre.4"
|
||||
sha2 = "0.11.0-pre.4"
|
||||
digest = "0.11.0-pre.9"
|
||||
libc = "0.2.171"
|
||||
cfg-if = "1.0.0"
|
||||
num-traits = "0.2.19"
|
||||
num-derive = "0.4.2"
|
||||
thiserror = "2.0.12"
|
||||
byteorder = "1.5.0"
|
||||
size = "0.5.0"
|
||||
bytemuck = "1.22.0"
|
||||
fdt = "0.1.5"
|
||||
const_format = "0.2.34"
|
||||
bit-set = "0.8.0"
|
||||
syn = "2.0.100"
|
||||
quote = "1.0.40"
|
||||
proc-macro2 = "1.0.94"
|
||||
argh = { version = "0.1.13", default-features = false }
|
||||
libz-rs-sys = { version = "0.5.0", features = ["export-symbols"] }
|
||||
libbz2-rs-sys = { version = "0.1.3" }
|
||||
pb-rs = { version = "0.10.0", default-features = false }
|
||||
quick-protobuf = "0.8.1"
|
||||
|
||||
# Rust crypto crates are tied together
|
||||
sha1 = "=0.11.0-pre.4"
|
||||
sha2 = "=0.11.0-pre.4"
|
||||
digest = "=0.11.0-pre.9"
|
||||
p256 = "0.14.0-pre.2"
|
||||
p384 = "0.14.0-pre.2"
|
||||
p521 = "0.14.0-pre.2"
|
||||
rsa = "0.10.0-pre.4"
|
||||
x509-cert = "0.3.0-pre.0"
|
||||
der = "0.8.0-rc.1"
|
||||
bytemuck = "1.16"
|
||||
fdt = "0.1"
|
||||
const_format = "0.2"
|
||||
bit-set = "0.8"
|
||||
syn = "2"
|
||||
quote = "1"
|
||||
proc-macro2 = "1"
|
||||
argh = { version = "0.1.13", default-features = false }
|
||||
libz-rs-sys = { version = "0.4.2", default-features = false, features=["c-allocator"] }
|
||||
libbz2-rs-sys = { version = "0.1.3", default-features = false, features = ["c-allocator"] }
|
||||
|
||||
# Pin version to prevent cargo update break builds
|
||||
# Pin version to prevent cargo update breaking builds
|
||||
block-buffer = "=0.11.0-rc.3"
|
||||
sec1 = "=0.8.0-rc.3"
|
||||
|
||||
[workspace.dependencies.pb-rs]
|
||||
git = "https://github.com/tafia/quick-protobuf.git"
|
||||
rev = "2f37d5a65504de7d716b5b28fd82219501a901a9"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.quick-protobuf]
|
||||
git = "https://github.com/tafia/quick-protobuf.git"
|
||||
rev = "2f37d5a65504de7d716b5b28fd82219501a901a9"
|
||||
[patch.crates-io]
|
||||
pb-rs = { git = "https://github.com/tafia/quick-protobuf.git" }
|
||||
quick-protobuf = { git = "https://github.com/tafia/quick-protobuf.git" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = "z"
|
||||
@@ -60,3 +59,4 @@ opt-level = "z"
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = true
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use cxx::{type_id, ExternType};
|
||||
use cxx::{ExternType, type_id};
|
||||
use libc::c_char;
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::min;
|
||||
use std::cmp::{Ordering, min};
|
||||
use std::ffi::{CStr, FromBytesWithNulError, OsStr};
|
||||
use std::fmt::{Debug, Display, Formatter, Write};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ops::Deref;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::Utf8Error;
|
||||
@@ -22,7 +22,7 @@ use crate::slice_from_ptr_mut;
|
||||
// Utf8CStrBufRef: reference to a fixed sized buffer
|
||||
// Utf8CStrBufArr<N>: fixed sized buffer allocated on the stack
|
||||
//
|
||||
// For easier usage, please use the helper functions in cstr_buf.
|
||||
// For easier usage, please use the helper functions in cstr::buf.
|
||||
//
|
||||
// In most cases, these are the types being used
|
||||
//
|
||||
@@ -37,11 +37,11 @@ use crate::slice_from_ptr_mut;
|
||||
|
||||
// Public helper functions
|
||||
|
||||
pub mod cstr_buf {
|
||||
pub mod buf {
|
||||
use super::{Utf8CStrBufArr, Utf8CStrBufRef, Utf8CString};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn with_capacity(capacity: usize) -> Utf8CString {
|
||||
pub fn dynamic(capacity: usize) -> Utf8CString {
|
||||
Utf8CString::with_capacity(capacity)
|
||||
}
|
||||
|
||||
@@ -68,9 +68,7 @@ pub mod cstr_buf {
|
||||
|
||||
// Trait definitions
|
||||
|
||||
pub trait Utf8CStrBuf:
|
||||
Write + AsRef<Utf8CStr> + AsMut<Utf8CStr> + Deref<Target = Utf8CStr> + DerefMut
|
||||
{
|
||||
pub trait Utf8CStrBuf: Write + AsRef<Utf8CStr> + Deref<Target = Utf8CStr> {
|
||||
// The length of the string without the terminating null character.
|
||||
// assert_true(len <= capacity - 1)
|
||||
fn len(&self) -> usize;
|
||||
@@ -82,11 +80,12 @@ pub trait Utf8CStrBuf:
|
||||
// 3. All bytes from 0 to len is valid UTF-8 and does not contain null
|
||||
unsafe fn set_len(&mut self, len: usize);
|
||||
fn push_str(&mut self, s: &str) -> usize;
|
||||
fn push_lossy(&mut self, s: &[u8]) -> usize;
|
||||
// The capacity of the internal buffer. The maximum string length this buffer can contain
|
||||
// is capacity - 1, because the last byte is reserved for the terminating null character.
|
||||
fn capacity(&self) -> usize;
|
||||
fn clear(&mut self);
|
||||
fn as_mut_ptr(&mut self) -> *mut c_char;
|
||||
fn truncate(&mut self, new_len: usize);
|
||||
|
||||
#[inline(always)]
|
||||
fn is_empty(&self) -> bool {
|
||||
@@ -94,65 +93,6 @@ pub trait Utf8CStrBuf:
|
||||
}
|
||||
}
|
||||
|
||||
trait Utf8CStrBufWithSlice: Utf8CStrBuf {
|
||||
fn buf(&self) -> &[u8];
|
||||
unsafe fn mut_buf(&mut self) -> &mut [u8];
|
||||
}
|
||||
|
||||
trait AsUtf8CStr {
|
||||
fn as_utf8_cstr(&self) -> &Utf8CStr;
|
||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr;
|
||||
}
|
||||
|
||||
impl<T: Utf8CStrBufWithSlice> AsUtf8CStr for T {
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr(&self) -> &Utf8CStr {
|
||||
// SAFETY: the internal buffer is always UTF-8 checked
|
||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||
unsafe { Utf8CStr::from_bytes_unchecked(self.buf().get_unchecked(..(self.len() + 1))) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr {
|
||||
// SAFETY: the internal buffer is always UTF-8 checked
|
||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||
unsafe {
|
||||
let len = self.len() + 1;
|
||||
Utf8CStr::from_bytes_unchecked_mut(self.mut_buf().get_unchecked_mut(..len))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation for Utf8CString
|
||||
|
||||
fn utf8_cstr_buf_append(buf: &mut dyn Utf8CStrBufWithSlice, s: &[u8]) -> usize {
|
||||
let mut used = buf.len();
|
||||
if used >= buf.capacity() - 1 {
|
||||
// Truncate
|
||||
return 0;
|
||||
}
|
||||
let dest = unsafe { &mut buf.mut_buf()[used..] };
|
||||
let len = min(s.len(), dest.len() - 1);
|
||||
if len > 0 {
|
||||
dest[..len].copy_from_slice(&s[..len]);
|
||||
}
|
||||
dest[len] = b'\0';
|
||||
used += len;
|
||||
unsafe { buf.set_len(used) };
|
||||
len
|
||||
}
|
||||
|
||||
fn utf8_cstr_append_lossy(buf: &mut dyn Utf8CStrBuf, s: &[u8]) -> usize {
|
||||
let mut len = 0_usize;
|
||||
for chunk in s.utf8_chunks() {
|
||||
len += buf.push_str(chunk.valid());
|
||||
if !chunk.invalid().is_empty() {
|
||||
len += buf.push_str(char::REPLACEMENT_CHARACTER.encode_utf8(&mut [0; 4]));
|
||||
}
|
||||
}
|
||||
len
|
||||
}
|
||||
|
||||
pub trait StringExt {
|
||||
fn nul_terminate(&mut self) -> &mut [u8];
|
||||
}
|
||||
@@ -206,23 +146,12 @@ impl Utf8CString {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsUtf8CStr for Utf8CString {
|
||||
impl AsRef<Utf8CStr> for Utf8CString {
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr(&self) -> &Utf8CStr {
|
||||
fn as_ref(&self) -> &Utf8CStr {
|
||||
// SAFETY: the internal string is always null terminated
|
||||
unsafe { mem::transmute(slice::from_raw_parts(self.0.as_ptr(), self.0.len() + 1)) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr {
|
||||
// SAFETY: the internal string is always null terminated
|
||||
unsafe {
|
||||
mem::transmute(slice::from_raw_parts_mut(
|
||||
self.0.as_mut_ptr(),
|
||||
self.0.len() + 1,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Utf8CStrBuf for Utf8CString {
|
||||
@@ -243,11 +172,6 @@ impl Utf8CStrBuf for Utf8CString {
|
||||
s.len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn push_lossy(&mut self, s: &[u8]) -> usize {
|
||||
utf8_cstr_append_lossy(self, s)
|
||||
}
|
||||
|
||||
fn capacity(&self) -> usize {
|
||||
self.0.capacity()
|
||||
}
|
||||
@@ -256,6 +180,15 @@ impl Utf8CStrBuf for Utf8CString {
|
||||
self.0.clear();
|
||||
self.0.nul_terminate();
|
||||
}
|
||||
|
||||
fn as_mut_ptr(&mut self) -> *mut c_char {
|
||||
self.0.as_mut_ptr().cast()
|
||||
}
|
||||
|
||||
fn truncate(&mut self, new_len: usize) {
|
||||
self.0.truncate(new_len);
|
||||
self.0.nul_terminate();
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Utf8CString {
|
||||
@@ -265,6 +198,12 @@ impl From<String> for Utf8CString {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Utf8CString {
|
||||
fn from(value: &str) -> Self {
|
||||
value.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<Utf8CStr> for Utf8CString {
|
||||
fn borrow(&self) -> &Utf8CStr {
|
||||
self.deref()
|
||||
@@ -290,18 +229,6 @@ impl<'a> From<&'a mut [u8]> for Utf8CStrBufRef<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Utf8CStrBufWithSlice for Utf8CStrBufRef<'_> {
|
||||
#[inline(always)]
|
||||
fn buf(&self) -> &[u8] {
|
||||
self.buf
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn mut_buf(&mut self) -> &mut [u8] {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
// UTF-8 validated + null terminated buffer on the stack
|
||||
pub struct Utf8CStrBufArr<const N: usize> {
|
||||
used: usize,
|
||||
@@ -317,18 +244,6 @@ impl<const N: usize> Utf8CStrBufArr<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Utf8CStrBufWithSlice for Utf8CStrBufArr<N> {
|
||||
#[inline(always)]
|
||||
fn buf(&self) -> &[u8] {
|
||||
&self.buf
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn mut_buf(&mut self) -> &mut [u8] {
|
||||
&mut self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Utf8CStrBufArr<4096> {
|
||||
fn default() -> Self {
|
||||
Utf8CStrBufArr::<4096>::new()
|
||||
@@ -360,7 +275,7 @@ impl Utf8CStr {
|
||||
Self::from_cstr(CStr::from_bytes_with_nul(buf)?)
|
||||
}
|
||||
|
||||
pub fn from_string(s: &mut String) -> &mut Utf8CStr {
|
||||
pub fn from_string(s: &mut String) -> &Utf8CStr {
|
||||
let buf = s.nul_terminate();
|
||||
// SAFETY: the null byte is explicitly added to the buffer
|
||||
unsafe { mem::transmute(buf) }
|
||||
@@ -371,11 +286,6 @@ impl Utf8CStr {
|
||||
unsafe { mem::transmute(buf) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn from_bytes_unchecked_mut(buf: &mut [u8]) -> &mut Utf8CStr {
|
||||
unsafe { mem::transmute(buf) }
|
||||
}
|
||||
|
||||
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> Result<&'a Utf8CStr, StrErr> {
|
||||
if ptr.is_null() {
|
||||
return Err(StrErr::NullPointerError);
|
||||
@@ -400,11 +310,6 @@ impl Utf8CStr {
|
||||
self.0.as_ptr().cast()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_mut_ptr(&mut self) -> *mut c_char {
|
||||
self.0.as_mut_ptr().cast()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_cstr(&self) -> &CStr {
|
||||
// SAFETY: Already validated as null terminated during construction
|
||||
@@ -417,13 +322,6 @@ impl Utf8CStr {
|
||||
// SAFETY: The length of the slice is at least 1 due to null termination check
|
||||
unsafe { str::from_utf8_unchecked(self.0.get_unchecked(..self.0.len() - 1)) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_str_mut(&mut self) -> &mut str {
|
||||
// SAFETY: Already UTF-8 validated during construction
|
||||
// SAFETY: The length of the slice is at least 1 due to null termination check
|
||||
unsafe { str::from_utf8_unchecked_mut(self.0.get_unchecked_mut(..self.0.len() - 1)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Utf8CStr {
|
||||
@@ -435,13 +333,6 @@ impl Deref for Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Utf8CStr {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_str_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Utf8CStr {
|
||||
type Owned = Utf8CString;
|
||||
|
||||
@@ -452,6 +343,12 @@ impl ToOwned for Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Utf8CStr> for Utf8CStr {
|
||||
fn as_ref(&self) -> &Utf8CStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Notice that we only implement ExternType on Utf8CStr *reference*
|
||||
unsafe impl ExternType for &Utf8CStr {
|
||||
type Id = type_id!("rust::Utf8CStr");
|
||||
@@ -470,150 +367,40 @@ const_assert_eq!(align_of::<&Utf8CStr>(), align_of::<[usize; 2]>());
|
||||
|
||||
// File system path extensions types
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct FsPath(Utf8CStr);
|
||||
|
||||
impl FsPath {
|
||||
#[inline(always)]
|
||||
pub fn from<T: AsRef<Utf8CStr> + ?Sized>(value: &T) -> &FsPath {
|
||||
unsafe { mem::transmute(value.as_ref()) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_mut<T: AsMut<Utf8CStr> + ?Sized>(value: &mut T) -> &mut FsPath {
|
||||
unsafe { mem::transmute(value.as_mut()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for FsPath {
|
||||
type Target = Utf8CStr;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Utf8CStr {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FsPath {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Utf8CStr {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct FsPathFollow(Utf8CStr);
|
||||
|
||||
impl Deref for FsPathFollow {
|
||||
type Target = Utf8CStr;
|
||||
|
||||
impl AsRef<Utf8CStr> for FsPathFollow {
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Utf8CStr {
|
||||
fn as_ref(&self) -> &Utf8CStr {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FsPathFollow {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Utf8CStr {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
enum Utf8CStrBufOwned<const N: usize> {
|
||||
Dynamic(Utf8CString),
|
||||
Fixed(Utf8CStrBufArr<N>),
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for Utf8CStrBufOwned<N> {
|
||||
type Target = dyn Utf8CStrBuf;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Utf8CStrBufOwned::Dynamic(s) => s,
|
||||
Utf8CStrBufOwned::Fixed(arr) => arr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DerefMut for Utf8CStrBufOwned<N> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Utf8CStrBufOwned::Dynamic(s) => s,
|
||||
Utf8CStrBufOwned::Fixed(arr) => arr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FsPathBuf<const N: usize>(Utf8CStrBufOwned<N>);
|
||||
|
||||
impl FsPathBuf<0> {
|
||||
pub fn new_dynamic(capacity: usize) -> Self {
|
||||
FsPathBuf(Utf8CStrBufOwned::Dynamic(Utf8CString::with_capacity(
|
||||
capacity,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FsPathBuf<4096> {
|
||||
fn default() -> Self {
|
||||
FsPathBuf(Utf8CStrBufOwned::Fixed(cstr_buf::default()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FsPathBuf<N> {
|
||||
pub fn new() -> Self {
|
||||
FsPathBuf(Utf8CStrBufOwned::Fixed(cstr_buf::new::<N>()))
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
|
||||
pub fn join<T: AsRef<str>>(mut self, path: T) -> Self {
|
||||
fn inner(buf: &mut dyn Utf8CStrBuf, path: &str) {
|
||||
if path.starts_with('/') {
|
||||
buf.clear();
|
||||
}
|
||||
if !buf.is_empty() && !buf.ends_with('/') {
|
||||
buf.push_str("/");
|
||||
}
|
||||
buf.push_str(path);
|
||||
}
|
||||
inner(self.0.deref_mut(), path.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn join_fmt<T: Display>(mut self, name: T) -> Self {
|
||||
self.0.write_fmt(format_args!("/{}", name)).ok();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for FsPathBuf<N> {
|
||||
type Target = FsPath;
|
||||
|
||||
fn deref(&self) -> &FsPath {
|
||||
FsPath::from(self.0.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DerefMut for FsPathBuf<N> {
|
||||
fn deref_mut(&mut self) -> &mut FsPath {
|
||||
FsPath::from_mut(self.0.deref_mut())
|
||||
}
|
||||
}
|
||||
|
||||
// Boilerplate trait implementations
|
||||
|
||||
macro_rules! impl_str {
|
||||
// impl<T: AsRef<Utf8CStr>> Deref<Target = Utf8CStr> for T { ... }
|
||||
macro_rules! impl_cstr_deref {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> AsRef<Utf8CStr> for $t {
|
||||
impl<$($g)*> Deref for $t {
|
||||
type Target = Utf8CStr;
|
||||
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &Utf8CStr {
|
||||
self
|
||||
fn deref(&self) -> &Utf8CStr {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_cstr_deref!(
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
(Utf8CString,)
|
||||
(FsPathFollow,)
|
||||
);
|
||||
|
||||
// impl<T: Deref<Target = Utf8CStr>> BoilerPlate for T { ... }
|
||||
macro_rules! impl_cstr_misc {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> AsRef<str> for $t {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
@@ -674,65 +461,59 @@ macro_rules! impl_str {
|
||||
self == other.as_cstr()
|
||||
}
|
||||
}
|
||||
impl<T: AsRef<Utf8CStr>, $($g)*> PartialEq<T> for $t {
|
||||
impl<T: AsRef<Utf8CStr> + ?Sized, $($g)*> PartialEq<T> for $t {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.as_bytes_with_nul() == other.as_ref().as_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
impl<$($g)*> Eq for $t {}
|
||||
impl<$($g)*> PartialOrd for $t {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl<$($g)*> Ord for $t {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_str().cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_str!(
|
||||
impl_cstr_misc!(
|
||||
(Utf8CStr,)
|
||||
(FsPath,)
|
||||
(FsPathFollow,)
|
||||
(FsPathBuf<N>, const N: usize)
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
(Utf8CString,)
|
||||
(FsPathFollow,)
|
||||
);
|
||||
|
||||
macro_rules! impl_str_buf {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> Write for $t {
|
||||
#[inline(always)]
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.push_str(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl<$($g)*> Deref for $t {
|
||||
type Target = Utf8CStr;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Utf8CStr {
|
||||
self.as_utf8_cstr()
|
||||
}
|
||||
}
|
||||
impl<$($g)*> DerefMut for $t {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Utf8CStr {
|
||||
self.as_utf8_cstr_mut()
|
||||
}
|
||||
}
|
||||
impl<$($g)*> AsMut<Utf8CStr> for $t {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut Utf8CStr {
|
||||
self.as_utf8_cstr_mut()
|
||||
}
|
||||
}
|
||||
)*}
|
||||
fn copy_cstr_truncate(dest: &mut [u8], src: &[u8]) -> usize {
|
||||
if dest.len() <= 1 {
|
||||
// Truncate
|
||||
return 0;
|
||||
}
|
||||
let len = min(src.len(), dest.len() - 1);
|
||||
if len > 0 {
|
||||
dest[..len].copy_from_slice(&src[..len]);
|
||||
}
|
||||
dest[len] = b'\0';
|
||||
len
|
||||
}
|
||||
|
||||
impl_str_buf!(
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
(Utf8CString,)
|
||||
);
|
||||
|
||||
macro_rules! impl_str_buf_with_slice {
|
||||
// impl<T> AsRef<Utf8CStr> for T { ... }
|
||||
// impl<T> Utf8CStrBuf for T { ... }
|
||||
macro_rules! impl_cstr_buf {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> AsRef<Utf8CStr> for $t {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &Utf8CStr {
|
||||
// SAFETY: the internal buffer is always UTF-8 checked
|
||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||
unsafe { Utf8CStr::from_bytes_unchecked(self.buf.get_unchecked(..(self.used + 1))) }
|
||||
}
|
||||
}
|
||||
impl<$($g)*> Utf8CStrBuf for $t {
|
||||
#[inline(always)]
|
||||
fn len(&self) -> usize {
|
||||
@@ -744,11 +525,11 @@ macro_rules! impl_str_buf_with_slice {
|
||||
}
|
||||
#[inline(always)]
|
||||
fn push_str(&mut self, s: &str) -> usize {
|
||||
utf8_cstr_buf_append(self, s.as_bytes())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn push_lossy(&mut self, s: &[u8]) -> usize {
|
||||
utf8_cstr_append_lossy(self, s)
|
||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||
let dest = unsafe { self.buf.get_unchecked_mut(self.used..) };
|
||||
let len = copy_cstr_truncate(dest, s.as_bytes());
|
||||
self.used += len;
|
||||
len
|
||||
}
|
||||
#[inline(always)]
|
||||
fn capacity(&self) -> usize {
|
||||
@@ -759,15 +540,45 @@ macro_rules! impl_str_buf_with_slice {
|
||||
self.buf[0] = b'\0';
|
||||
self.used = 0;
|
||||
}
|
||||
#[inline(always)]
|
||||
fn as_mut_ptr(&mut self) -> *mut c_char {
|
||||
self.buf.as_mut_ptr().cast()
|
||||
}
|
||||
fn truncate(&mut self, new_len: usize) {
|
||||
if self.used <= new_len {
|
||||
return;
|
||||
}
|
||||
self.buf[new_len] = b'\0';
|
||||
self.used = new_len;
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_str_buf_with_slice!(
|
||||
impl_cstr_buf!(
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
);
|
||||
|
||||
// impl<T: Utf8CStrBuf> Write for T { ... }
|
||||
macro_rules! impl_cstr_buf_write {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> Write for $t {
|
||||
#[inline(always)]
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.push_str(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_cstr_buf_write!(
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
(Utf8CString,)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cstr {
|
||||
($str:expr) => {{
|
||||
@@ -781,14 +592,5 @@ macro_rules! cstr {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! raw_cstr {
|
||||
($str:expr) => {{
|
||||
$crate::cstr!($str).as_ptr()
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! path {
|
||||
($str:expr) => {{
|
||||
$crate::FsPath::from($crate::cstr!($str))
|
||||
}};
|
||||
($str:expr) => {{ $crate::cstr!($str).as_ptr() }};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Functions in this file are only for exporting to C++, DO NOT USE IN RUST
|
||||
|
||||
use std::io;
|
||||
use std::os::fd::{BorrowedFd, OwnedFd, RawFd};
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
@@ -9,14 +8,14 @@ use libc::{c_char, mode_t};
|
||||
use crate::files::map_file_at;
|
||||
pub(crate) use crate::xwrap::*;
|
||||
use crate::{
|
||||
clone_attr, cstr, cstr_buf, fclone_attr, fd_path, map_fd, map_file, slice_from_ptr,
|
||||
CxxResultExt, Directory, FsPath, Utf8CStr,
|
||||
CxxResultExt, Directory, OsResultStatic, Utf8CStr, clone_attr, cstr, fclone_attr, fd_path,
|
||||
map_fd, map_file, slice_from_ptr,
|
||||
};
|
||||
|
||||
pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
|
||||
let mut buf = cstr_buf::wrap(buf);
|
||||
let mut buf = cstr::buf::wrap(buf);
|
||||
fd_path(fd, &mut buf)
|
||||
.log_cxx_with_msg(|w| w.write_str("fd_path failed"))
|
||||
.log_cxx()
|
||||
.map_or(-1_isize, |_| buf.len() as isize)
|
||||
}
|
||||
|
||||
@@ -24,11 +23,11 @@ pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
|
||||
unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => {
|
||||
let mut buf = cstr_buf::wrap_ptr(buf, bufsz);
|
||||
FsPath::from(p)
|
||||
.realpath(&mut buf)
|
||||
.map_or(-1, |_| buf.len() as isize)
|
||||
Ok(path) => {
|
||||
let mut buf = cstr::buf::wrap_ptr(buf, bufsz);
|
||||
path.realpath(&mut buf)
|
||||
.log_cxx()
|
||||
.map_or(-1_isize, |_| buf.len() as isize)
|
||||
}
|
||||
Err(_) => -1,
|
||||
}
|
||||
@@ -39,7 +38,7 @@ unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: us
|
||||
unsafe extern "C" fn mkdirs_for_cxx(path: *const c_char, mode: mode_t) -> i32 {
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => FsPath::from(p).mkdirs(mode).map_or(-1, |_| 0),
|
||||
Ok(path) => path.mkdirs(mode).map_or(-1, |_| 0),
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
@@ -49,7 +48,7 @@ unsafe extern "C" fn mkdirs_for_cxx(path: *const c_char, mode: mode_t) -> i32 {
|
||||
unsafe extern "C" fn rm_rf_for_cxx(path: *const c_char) -> bool {
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => FsPath::from(p).remove_all().is_ok(),
|
||||
Ok(path) => path.remove_all().is_ok(),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
@@ -57,7 +56,7 @@ unsafe extern "C" fn rm_rf_for_cxx(path: *const c_char) -> bool {
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn frm_rf(fd: OwnedFd) -> bool {
|
||||
fn inner(fd: OwnedFd) -> io::Result<()> {
|
||||
fn inner(fd: OwnedFd) -> OsResultStatic<()> {
|
||||
Directory::try_from(fd)?.remove_all()
|
||||
}
|
||||
inner(fd).is_ok()
|
||||
@@ -83,7 +82,7 @@ pub(crate) fn map_fd_for_cxx(fd: RawFd, sz: usize, rw: bool) -> &'static mut [u8
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn readlinkat_for_cxx(
|
||||
pub(crate) unsafe fn readlinkat(
|
||||
dirfd: RawFd,
|
||||
path: *const c_char,
|
||||
buf: *mut u8,
|
||||
@@ -115,14 +114,7 @@ unsafe extern "C" fn cp_afc_for_cxx(src: *const c_char, dest: *const c_char) ->
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return src
|
||||
.copy_to(dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("cp_afc {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
return src.copy_to(dest).log_cxx().is_ok();
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -134,14 +126,7 @@ unsafe extern "C" fn mv_path_for_cxx(src: *const c_char, dest: *const c_char) ->
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return src
|
||||
.move_to(dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("mv_path {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
return src.move_to(dest).log_cxx().is_ok();
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -153,14 +138,7 @@ unsafe extern "C" fn link_path_for_cxx(src: *const c_char, dest: *const c_char)
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return src
|
||||
.link_to(dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("link_path {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
return src.link_to(dest).log_cxx().is_ok();
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -172,13 +150,7 @@ unsafe extern "C" fn clone_attr_for_cxx(src: *const c_char, dest: *const c_char)
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return clone_attr(src, dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("clone_attr {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
return clone_attr(src, dest).log_cxx().is_ok();
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -187,9 +159,7 @@ unsafe extern "C" fn clone_attr_for_cxx(src: *const c_char, dest: *const c_char)
|
||||
|
||||
#[unsafe(export_name = "fclone_attr")]
|
||||
unsafe extern "C" fn fclone_attr_for_cxx(a: RawFd, b: RawFd) -> bool {
|
||||
fclone_attr(a, b)
|
||||
.log_cxx_with_msg(|w| w.write_str("fclone_attr failed"))
|
||||
.is_ok()
|
||||
fclone_attr(a, b).log_cxx().is_ok()
|
||||
}
|
||||
|
||||
#[unsafe(export_name = "cxx$utf8str$new")]
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
use crate::cxx_extern::readlinkat_for_cxx;
|
||||
use crate::cxx_extern::readlinkat;
|
||||
use crate::{
|
||||
cstr, cstr_buf, errno, fd_path, fd_set_attr, FileAttr, FsPath, LibcReturn, Utf8CStr,
|
||||
Utf8CStrBuf,
|
||||
FsPathBuilder, LibcReturn, OsError, OsResult, OsResultStatic, Utf8CStr, Utf8CStrBuf, cstr,
|
||||
errno, fd_path, fd_set_attr,
|
||||
};
|
||||
use libc::{dirent, O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY};
|
||||
use std::ffi::CStr;
|
||||
use libc::{EEXIST, O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, dirent, mode_t};
|
||||
use std::fs::File;
|
||||
use std::ops::Deref;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||
use std::{io, mem, slice};
|
||||
use std::ptr::NonNull;
|
||||
use std::{mem, slice};
|
||||
|
||||
pub struct DirEntry<'a> {
|
||||
dir: &'a Directory,
|
||||
entry: &'a dirent,
|
||||
dir: BorrowedDirectory<'a>,
|
||||
entry: NonNull<dirent>,
|
||||
d_name_len: usize,
|
||||
}
|
||||
|
||||
impl DirEntry<'_> {
|
||||
pub fn name(&self) -> &CStr {
|
||||
pub fn as_ptr(&self) -> *mut dirent {
|
||||
self.entry.as_ptr()
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &Utf8CStr {
|
||||
unsafe {
|
||||
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
|
||||
Utf8CStr::from_bytes_unchecked(slice::from_raw_parts(
|
||||
self.d_name.as_ptr().cast(),
|
||||
self.d_name_len,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
self.dir.path(buf)?;
|
||||
buf.push_str("/");
|
||||
buf.push_lossy(self.name().to_bytes());
|
||||
Ok(())
|
||||
pub fn resolve_path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||
self.dir.path_at(self.name(), buf)
|
||||
}
|
||||
|
||||
pub fn is_dir(&self) -> bool {
|
||||
@@ -61,69 +63,37 @@ impl DirEntry<'_> {
|
||||
self.d_type == libc::DT_SOCK
|
||||
}
|
||||
|
||||
pub fn unlink(&self) -> io::Result<()> {
|
||||
pub fn unlink(&self) -> OsResult<()> {
|
||||
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
||||
unsafe {
|
||||
libc::unlinkat(self.dir.as_raw_fd(), self.d_name.as_ptr(), flag).check_os_err()?;
|
||||
}
|
||||
Ok(())
|
||||
self.dir.unlink_at(self.name(), flag)
|
||||
}
|
||||
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
buf.clear();
|
||||
unsafe {
|
||||
let r = readlinkat_for_cxx(
|
||||
self.dir.as_raw_fd(),
|
||||
self.d_name.as_ptr(),
|
||||
buf.as_mut_ptr().cast(),
|
||||
buf.capacity(),
|
||||
)
|
||||
.check_os_err()? as usize;
|
||||
buf.set_len(r);
|
||||
}
|
||||
Ok(())
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
self.dir.read_link_at(self.name(), buf)
|
||||
}
|
||||
|
||||
unsafe fn open_fd(&self, flags: i32) -> io::Result<RawFd> {
|
||||
unsafe { self.dir.open_raw_fd(self.name(), flags, 0) }
|
||||
}
|
||||
|
||||
pub fn open_as_dir(&self) -> io::Result<Directory> {
|
||||
pub fn open_as_dir(&self) -> OsResult<Directory> {
|
||||
if !self.is_dir() {
|
||||
return Err(io::Error::from(io::ErrorKind::NotADirectory));
|
||||
return Err(OsError::with_os_error(
|
||||
libc::ENOTDIR,
|
||||
"fdopendir",
|
||||
Some(self.name()),
|
||||
None,
|
||||
));
|
||||
}
|
||||
unsafe { Directory::try_from(OwnedFd::from_raw_fd(self.open_fd(O_RDONLY)?)) }
|
||||
self.dir.open_as_dir_at(self.name())
|
||||
}
|
||||
|
||||
pub fn open_as_file(&self, flags: i32) -> io::Result<File> {
|
||||
pub fn open_as_file(&self, flags: i32) -> OsResult<File> {
|
||||
if self.is_dir() {
|
||||
return Err(io::Error::from(io::ErrorKind::IsADirectory));
|
||||
return Err(OsError::with_os_error(
|
||||
libc::EISDIR,
|
||||
"open_as_file",
|
||||
Some(self.name()),
|
||||
None,
|
||||
));
|
||||
}
|
||||
unsafe { Ok(File::from_raw_fd(self.open_fd(flags)?)) }
|
||||
}
|
||||
|
||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
||||
let mut path = cstr_buf::default();
|
||||
self.path(&mut path)?;
|
||||
FsPath::from(&path).get_attr()
|
||||
}
|
||||
|
||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
||||
let mut path = cstr_buf::default();
|
||||
self.path(&mut path)?;
|
||||
FsPath::from(&path).set_attr(attr)
|
||||
}
|
||||
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
let mut path = cstr_buf::default();
|
||||
self.path(&mut path)?;
|
||||
FsPath::from(&path).get_secontext(con)
|
||||
}
|
||||
|
||||
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||
let mut path = cstr_buf::default();
|
||||
self.path(&mut path)?;
|
||||
FsPath::from(&path).set_secontext(con)
|
||||
self.dir.open_as_file_at(self.name(), flags, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,12 +101,37 @@ impl Deref for DirEntry<'_> {
|
||||
type Target = dirent;
|
||||
|
||||
fn deref(&self) -> &dirent {
|
||||
self.entry
|
||||
unsafe { self.entry.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Directory {
|
||||
dirp: *mut libc::DIR,
|
||||
inner: NonNull<libc::DIR>,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct BorrowedDirectory<'a> {
|
||||
inner: NonNull<libc::DIR>,
|
||||
phantom: PhantomData<&'a Directory>,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedDirectory<'_> {
|
||||
type Target = Directory;
|
||||
|
||||
fn deref(&self) -> &Directory {
|
||||
// SAFETY: layout of NonNull<libc::DIR> is the same as Directory
|
||||
// SAFETY: the lifetime of the raw pointer is tracked in the PhantomData
|
||||
unsafe { mem::transmute(&self.inner) }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for BorrowedDirectory<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Directory {
|
||||
// SAFETY: layout of NonNull<libc::DIR> is the same as Directory
|
||||
// SAFETY: the lifetime of the raw pointer is tracked in the PhantomData
|
||||
unsafe { mem::transmute(&mut self.inner) }
|
||||
}
|
||||
}
|
||||
|
||||
pub enum WalkResult {
|
||||
@@ -146,32 +141,60 @@ pub enum WalkResult {
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
pub fn open(path: &Utf8CStr) -> io::Result<Directory> {
|
||||
let dirp = unsafe { libc::opendir(path.as_ptr()) }.check_os_err()?;
|
||||
Ok(Directory { dirp })
|
||||
fn borrow(&self) -> BorrowedDirectory {
|
||||
BorrowedDirectory {
|
||||
inner: self.inner,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self) -> io::Result<Option<DirEntry<'_>>> {
|
||||
fn openat<'a>(&self, name: &'a Utf8CStr, flags: i32, mode: u32) -> OsResult<'a, OwnedFd> {
|
||||
unsafe {
|
||||
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode)
|
||||
.as_os_result("openat", Some(name), None)
|
||||
.map(|fd| OwnedFd::from_raw_fd(fd))
|
||||
}
|
||||
}
|
||||
|
||||
fn path_at(&self, name: &Utf8CStr, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||
self.resolve_path(buf)?;
|
||||
buf.append_path(name);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
pub fn open(path: &Utf8CStr) -> OsResult<Directory> {
|
||||
let dirp = unsafe { libc::opendir(path.as_ptr()) };
|
||||
let dirp = dirp.as_os_result("opendir", Some(path), None)?;
|
||||
Ok(Directory { inner: dirp })
|
||||
}
|
||||
|
||||
pub fn read(&mut self) -> OsResult<'static, Option<DirEntry>> {
|
||||
*errno() = 0;
|
||||
let e = unsafe { libc::readdir(self.dirp) };
|
||||
let e = unsafe { libc::readdir(self.inner.as_ptr()) };
|
||||
if e.is_null() {
|
||||
return if *errno() != 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
Err(OsError::last_os_error("readdir", None, None))
|
||||
} else {
|
||||
Ok(None)
|
||||
};
|
||||
}
|
||||
// Skip both "." and ".."
|
||||
// Skip non UTF-8 entries, ".", and ".."
|
||||
unsafe {
|
||||
let entry = &*e;
|
||||
let d_name = CStr::from_ptr(entry.d_name.as_ptr());
|
||||
if d_name == cstr!(".") || d_name == cstr!("..") {
|
||||
|
||||
let Ok(name) = Utf8CStr::from_ptr(entry.d_name.as_ptr()) else {
|
||||
return self.read();
|
||||
};
|
||||
|
||||
if name == "." || name == ".." {
|
||||
self.read()
|
||||
} else {
|
||||
let e = DirEntry {
|
||||
dir: self,
|
||||
entry,
|
||||
d_name_len: d_name.to_bytes_with_nul().len(),
|
||||
dir: self.borrow(),
|
||||
entry: NonNull::from(entry),
|
||||
d_name_len: name.as_bytes_with_nul().len(),
|
||||
};
|
||||
Ok(Some(e))
|
||||
}
|
||||
@@ -179,23 +202,81 @@ impl Directory {
|
||||
}
|
||||
|
||||
pub fn rewind(&mut self) {
|
||||
unsafe { libc::rewinddir(self.dirp) }
|
||||
unsafe { libc::rewinddir(self.inner.as_ptr()) };
|
||||
}
|
||||
|
||||
unsafe fn open_raw_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result<RawFd> {
|
||||
pub fn open_as_dir_at<'a>(&self, name: &'a Utf8CStr) -> OsResult<'a, Directory> {
|
||||
let fd = self.openat(name, O_RDONLY, 0)?;
|
||||
Directory::try_from(fd).map_err(|e| e.set_args(Some(name), None))
|
||||
}
|
||||
|
||||
pub fn open_as_file_at<'a>(
|
||||
&self,
|
||||
name: &'a Utf8CStr,
|
||||
flags: i32,
|
||||
mode: u32,
|
||||
) -> OsResult<'a, File> {
|
||||
let fd = self.openat(name, flags, mode)?;
|
||||
Ok(File::from(fd))
|
||||
}
|
||||
|
||||
pub fn read_link_at<'a>(
|
||||
&self,
|
||||
name: &'a Utf8CStr,
|
||||
buf: &mut dyn Utf8CStrBuf,
|
||||
) -> OsResult<'a, ()> {
|
||||
buf.clear();
|
||||
unsafe {
|
||||
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode).check_os_err()
|
||||
let r = readlinkat(
|
||||
self.as_raw_fd(),
|
||||
name.as_ptr(),
|
||||
buf.as_mut_ptr().cast(),
|
||||
buf.capacity(),
|
||||
)
|
||||
.as_os_result("readlinkat", Some(name), None)? as usize;
|
||||
buf.set_len(r);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mkdir_at<'a>(&self, name: &'a Utf8CStr, mode: mode_t) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
if libc::mkdirat(self.as_raw_fd(), name.as_ptr(), mode as mode_t) < 0
|
||||
&& *errno() != EEXIST
|
||||
{
|
||||
return Err(OsError::last_os_error("mkdirat", Some(name), None));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ln -s target self/name
|
||||
pub fn create_symlink_at<'a>(
|
||||
&self,
|
||||
name: &'a Utf8CStr,
|
||||
target: &'a Utf8CStr,
|
||||
) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::symlinkat(target.as_ptr(), self.as_raw_fd(), name.as_ptr()).check_os_err(
|
||||
"symlinkat",
|
||||
Some(target),
|
||||
Some(name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_fd(&self, name: &Utf8CStr, flags: i32, mode: i32) -> io::Result<OwnedFd> {
|
||||
pub fn unlink_at<'a>(&self, name: &'a Utf8CStr, flag: i32) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
self.open_raw_fd(name.as_cstr(), flags, mode)
|
||||
.map(|fd| OwnedFd::from_raw_fd(fd))
|
||||
libc::unlinkat(self.as_raw_fd(), name.as_ptr(), flag).check_os_err(
|
||||
"unlinkat",
|
||||
Some(name),
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn contains_path(&self, path: &CStr) -> bool {
|
||||
pub fn contains_path(&self, path: &Utf8CStr) -> bool {
|
||||
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
|
||||
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
|
||||
// Use fstatat to check the existence of a file instead.
|
||||
@@ -210,25 +291,25 @@ impl Directory {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
pub fn resolve_path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||
fd_path(self.as_raw_fd(), buf)
|
||||
}
|
||||
|
||||
pub fn post_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
pub fn post_order_walk<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||
&mut self,
|
||||
mut f: F,
|
||||
) -> io::Result<WalkResult> {
|
||||
) -> OsResultStatic<WalkResult> {
|
||||
self.post_order_walk_impl(&mut f)
|
||||
}
|
||||
|
||||
pub fn pre_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
pub fn pre_order_walk<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||
&mut self,
|
||||
mut f: F,
|
||||
) -> io::Result<WalkResult> {
|
||||
) -> OsResultStatic<WalkResult> {
|
||||
self.pre_order_walk_impl(&mut f)
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self) -> io::Result<()> {
|
||||
pub fn remove_all(&mut self) -> OsResultStatic<()> {
|
||||
self.post_order_walk(|e| {
|
||||
e.unlink()?;
|
||||
Ok(WalkResult::Continue)
|
||||
@@ -236,60 +317,20 @@ impl Directory {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn copy_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||
while let Some(ref e) = self.read()? {
|
||||
let attr = e.get_attr()?;
|
||||
let new_entry = DirEntry {
|
||||
dir,
|
||||
entry: e.entry,
|
||||
d_name_len: e.d_name_len,
|
||||
};
|
||||
if e.is_dir() {
|
||||
unsafe {
|
||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
||||
}
|
||||
let mut src = e.open_as_dir()?;
|
||||
let dest = new_entry.open_as_dir()?;
|
||||
src.copy_into(&dest)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else if e.is_file() {
|
||||
let mut src = e.open_as_file(O_RDONLY)?;
|
||||
let mut dest = unsafe {
|
||||
File::from_raw_fd(dir.open_raw_fd(
|
||||
e.name(),
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
0o777,
|
||||
)?)
|
||||
};
|
||||
std::io::copy(&mut src, &mut dest)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else if e.is_symlink() {
|
||||
let mut path = cstr_buf::default();
|
||||
e.read_link(&mut path)?;
|
||||
unsafe {
|
||||
libc::symlinkat(path.as_ptr(), dir.as_raw_fd(), e.d_name.as_ptr())
|
||||
.as_os_err()?;
|
||||
}
|
||||
new_entry.set_attr(&attr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
pub fn copy_into(&mut self, dir: &Directory) -> OsResultStatic<()> {
|
||||
let mut buf = cstr::buf::default();
|
||||
self.copy_into_impl(dir, &mut buf)
|
||||
}
|
||||
|
||||
pub fn move_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||
pub fn move_into(&mut self, dir: &Directory) -> OsResultStatic<()> {
|
||||
let dir_fd = self.as_raw_fd();
|
||||
while let Some(ref e) = self.read()? {
|
||||
if e.is_dir() && dir.contains_path(e.name()) {
|
||||
// Destination folder exists, needs recursive move
|
||||
let mut src = e.open_as_dir()?;
|
||||
let new_entry = DirEntry {
|
||||
dir,
|
||||
entry: e.entry,
|
||||
d_name_len: e.d_name_len,
|
||||
};
|
||||
let dest = new_entry.open_as_dir()?;
|
||||
let dest = dir.open_as_dir_at(e.name())?;
|
||||
src.move_into(&dest)?;
|
||||
return e.unlink();
|
||||
return Ok(e.unlink()?);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
@@ -299,51 +340,23 @@ impl Directory {
|
||||
dir.as_raw_fd(),
|
||||
e.d_name.as_ptr(),
|
||||
)
|
||||
.as_os_err()?;
|
||||
.check_os_err("renameat", Some(e.name()), None)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn link_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||
let dir_fd = self.as_raw_fd();
|
||||
while let Some(ref e) = self.read()? {
|
||||
if e.is_dir() {
|
||||
unsafe {
|
||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
||||
}
|
||||
let attr = e.get_attr()?;
|
||||
let new_entry = DirEntry {
|
||||
dir,
|
||||
entry: e.entry,
|
||||
d_name_len: e.d_name_len,
|
||||
};
|
||||
let mut src = e.open_as_dir()?;
|
||||
let dest = new_entry.open_as_dir()?;
|
||||
src.link_into(&dest)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else {
|
||||
unsafe {
|
||||
libc::linkat(
|
||||
dir_fd,
|
||||
e.d_name.as_ptr(),
|
||||
dir.as_raw_fd(),
|
||||
e.d_name.as_ptr(),
|
||||
0,
|
||||
)
|
||||
.as_os_err()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
pub fn link_into(&mut self, dir: &Directory) -> OsResultStatic<()> {
|
||||
let mut buf = cstr::buf::default();
|
||||
self.link_into_impl(dir, &mut buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
fn post_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
fn post_order_walk_impl<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
) -> io::Result<WalkResult> {
|
||||
) -> OsResultStatic<WalkResult> {
|
||||
use WalkResult::*;
|
||||
loop {
|
||||
match self.read()? {
|
||||
@@ -365,10 +378,10 @@ impl Directory {
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
fn pre_order_walk_impl<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
) -> io::Result<WalkResult> {
|
||||
) -> OsResultStatic<WalkResult> {
|
||||
use WalkResult::*;
|
||||
loop {
|
||||
match self.read()? {
|
||||
@@ -388,20 +401,82 @@ impl Directory {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_into_impl(
|
||||
&mut self,
|
||||
dest_dir: &Directory,
|
||||
buf: &mut dyn Utf8CStrBuf,
|
||||
) -> OsResultStatic<()> {
|
||||
while let Some(ref e) = self.read()? {
|
||||
e.resolve_path(buf)?;
|
||||
let attr = buf.get_attr()?;
|
||||
if e.is_dir() {
|
||||
dest_dir.mkdir_at(e.name(), 0o777)?;
|
||||
let mut src = e.open_as_dir()?;
|
||||
let dest = dest_dir.open_as_dir_at(e.name())?;
|
||||
src.copy_into_impl(&dest, buf)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else if e.is_file() {
|
||||
let mut src = e.open_as_file(O_RDONLY)?;
|
||||
let mut dest =
|
||||
dest_dir.open_as_file_at(e.name(), O_WRONLY | O_CREAT | O_TRUNC, 0o777)?;
|
||||
std::io::copy(&mut src, &mut dest)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else if e.is_symlink() {
|
||||
e.read_link(buf)?;
|
||||
dest_dir.create_symlink_at(e.name(), buf)?;
|
||||
dest_dir.path_at(e.name(), buf)?;
|
||||
buf.set_attr(&attr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn link_into_impl(
|
||||
&mut self,
|
||||
dest_dir: &Directory,
|
||||
buf: &mut dyn Utf8CStrBuf,
|
||||
) -> OsResultStatic<()> {
|
||||
let dir_fd = self.as_raw_fd();
|
||||
while let Some(ref e) = self.read()? {
|
||||
if e.is_dir() {
|
||||
dest_dir.mkdir_at(e.name(), 0o777)?;
|
||||
e.resolve_path(buf)?;
|
||||
let attr = buf.get_attr()?;
|
||||
let mut src = e.open_as_dir()?;
|
||||
let dest = dest_dir.open_as_dir_at(e.name())?;
|
||||
src.link_into_impl(&dest, buf)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else {
|
||||
unsafe {
|
||||
libc::linkat(
|
||||
dir_fd,
|
||||
e.d_name.as_ptr(),
|
||||
dest_dir.as_raw_fd(),
|
||||
e.d_name.as_ptr(),
|
||||
0,
|
||||
)
|
||||
.check_os_err("linkat", Some(e.name()), None)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<OwnedFd> for Directory {
|
||||
type Error = io::Error;
|
||||
type Error = OsError<'static>;
|
||||
|
||||
fn try_from(fd: OwnedFd) -> io::Result<Self> {
|
||||
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) }.check_os_err()?;
|
||||
Ok(Directory { dirp })
|
||||
fn try_from(fd: OwnedFd) -> OsResult<'static, Self> {
|
||||
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) };
|
||||
let dirp = dirp.as_os_result("fdopendir", None, None)?;
|
||||
Ok(Directory { inner: dirp })
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Directory {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
unsafe { libc::dirfd(self.dirp) }
|
||||
unsafe { libc::dirfd(self.inner.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +489,7 @@ impl AsFd for Directory {
|
||||
impl Drop for Directory {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
libc::closedir(self.dirp);
|
||||
libc::closedir(self.inner.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ string resolve_preinit_dir(const char *base_dir) {
|
||||
if (access((dir + "/unencrypted").data(), F_OK) == 0) {
|
||||
dir += "/unencrypted/magisk";
|
||||
} else if (access((dir + "/adb").data(), F_OK) == 0) {
|
||||
dir += "/adb/modules";
|
||||
dir += "/adb";
|
||||
} else if (access((dir + "/watchdog").data(), F_OK) == 0) {
|
||||
dir += "/watchdog/magisk";
|
||||
} else {
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
use crate::{
|
||||
cstr_buf, errno, error, Directory, FsPath, FsPathBuf, FsPathFollow, LibcReturn, Utf8CStr,
|
||||
Utf8CStrBuf,
|
||||
Directory, FsPathFollow, LibcReturn, OsError, OsResult, OsResultStatic, Utf8CStr, Utf8CStrBuf,
|
||||
cstr, errno, error,
|
||||
};
|
||||
use bytemuck::{bytes_of, bytes_of_mut, Pod};
|
||||
use bytemuck::{Pod, bytes_of, bytes_of_mut};
|
||||
use libc::{
|
||||
c_uint, makedev, mode_t, stat, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY,
|
||||
O_RDWR, O_TRUNC, O_WRONLY,
|
||||
EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, c_uint,
|
||||
makedev, mode_t, stat,
|
||||
};
|
||||
use mem::MaybeUninit;
|
||||
use num_traits::AsPrimitive;
|
||||
use std::cmp::min;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Display;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
|
||||
use std::os::fd::{AsFd, BorrowedFd};
|
||||
@@ -19,28 +20,6 @@ use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
||||
use std::path::Path;
|
||||
use std::{io, mem, ptr, slice};
|
||||
|
||||
pub fn __open_fd_impl(path: &Utf8CStr, flags: i32, mode: mode_t) -> io::Result<OwnedFd> {
|
||||
unsafe {
|
||||
let fd = libc::open(path.as_ptr(), flags, mode as c_uint).check_os_err()?;
|
||||
Ok(OwnedFd::from_raw_fd(fd))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! open_fd {
|
||||
($path:expr, $flags:expr) => {
|
||||
$crate::__open_fd_impl($path, $flags, 0)
|
||||
};
|
||||
($path:expr, $flags:expr, $mode:expr) => {
|
||||
$crate::__open_fd_impl($path, $flags, $mode)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fd_path(fd: RawFd, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
let path = FsPathBuf::default().join("/proc/self/fd").join_fmt(fd);
|
||||
path.read_link(buf)
|
||||
}
|
||||
|
||||
pub trait ReadExt {
|
||||
fn skip(&mut self, len: usize) -> io::Result<()>;
|
||||
fn read_pod<F: Pod>(&mut self, data: &mut F) -> io::Result<()>;
|
||||
@@ -137,6 +116,24 @@ impl<T: Write> WriteExt for T {
|
||||
}
|
||||
}
|
||||
|
||||
fn open_fd(path: &Utf8CStr, flags: i32, mode: mode_t) -> OsResult<OwnedFd> {
|
||||
unsafe {
|
||||
let fd = libc::open(path.as_ptr(), flags, mode as c_uint).as_os_result(
|
||||
"open",
|
||||
Some(path),
|
||||
None,
|
||||
)?;
|
||||
Ok(OwnedFd::from_raw_fd(fd))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd_path(fd: RawFd, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||
let path = cstr::buf::new::<64>()
|
||||
.join_path("/proc/self/fd")
|
||||
.join_path_fmt(fd);
|
||||
path.read_link(buf).map_err(|e| e.set_args(None, None))
|
||||
}
|
||||
|
||||
pub struct FileAttr {
|
||||
pub st: libc::stat,
|
||||
#[cfg(feature = "selinux")]
|
||||
@@ -189,17 +186,17 @@ impl FileAttr {
|
||||
|
||||
const XATTR_NAME_SELINUX: &CStr = c"security.selinux";
|
||||
|
||||
impl FsPath {
|
||||
impl Utf8CStr {
|
||||
pub fn follow_link(&self) -> &FsPathFollow {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
|
||||
pub fn open(&self, flags: i32) -> io::Result<File> {
|
||||
Ok(File::from(open_fd!(self, flags)?))
|
||||
pub fn open(&self, flags: i32) -> OsResult<File> {
|
||||
Ok(File::from(open_fd(self, flags, 0)?))
|
||||
}
|
||||
|
||||
pub fn create(&self, flags: i32, mode: mode_t) -> io::Result<File> {
|
||||
Ok(File::from(open_fd!(self, flags, mode)?))
|
||||
pub fn create(&self, flags: i32, mode: mode_t) -> OsResult<File> {
|
||||
Ok(File::from(open_fd(self, O_CREAT | flags, mode)?))
|
||||
}
|
||||
|
||||
pub fn exists(&self) -> bool {
|
||||
@@ -209,76 +206,86 @@ impl FsPath {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename_to<T: AsRef<Utf8CStr>>(&self, name: T) -> io::Result<()> {
|
||||
unsafe { libc::rename(self.as_ptr(), name.as_ref().as_ptr()).as_os_err() }
|
||||
pub fn rename_to<'a>(&'a self, name: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::rename(self.as_ptr(), name.as_ptr()).check_os_err(
|
||||
"rename",
|
||||
Some(self),
|
||||
Some(name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&self) -> io::Result<()> {
|
||||
unsafe { libc::remove(self.as_ptr()).as_os_err() }
|
||||
pub fn remove(&self) -> OsResult<()> {
|
||||
unsafe { libc::remove(self.as_ptr()).check_os_err("remove", Some(self), None) }
|
||||
}
|
||||
|
||||
pub fn remove_all(&self) -> io::Result<()> {
|
||||
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)?)?;
|
||||
let mut dir = Directory::try_from(open_fd(self, O_RDONLY | O_CLOEXEC, 0)?)?;
|
||||
dir.remove_all()?;
|
||||
}
|
||||
self.remove()
|
||||
Ok(self.remove()?)
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
buf.clear();
|
||||
unsafe {
|
||||
let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr(), buf.capacity() - 1)
|
||||
.check_os_err()? as isize;
|
||||
.as_os_result("readlink", Some(self), None)? as isize;
|
||||
*(buf.as_mut_ptr().offset(r) as *mut u8) = b'\0';
|
||||
buf.set_len(r as usize);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mkdir(&self, mode: mode_t) -> io::Result<()> {
|
||||
pub fn mkdir(&self, mode: mode_t) -> OsResult<()> {
|
||||
unsafe {
|
||||
if libc::mkdir(self.as_ptr(), mode) < 0 {
|
||||
if *errno() == EEXIST {
|
||||
libc::chmod(self.as_ptr(), mode).as_os_err()?;
|
||||
libc::chmod(self.as_ptr(), mode).check_os_err("chmod", Some(self), None)?;
|
||||
} else {
|
||||
return Err(io::Error::last_os_error());
|
||||
return Err(OsError::last_os_error("mkdir", Some(self), None));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mkdirs(&self, mode: mode_t) -> io::Result<()> {
|
||||
pub fn mkdirs(&self, mode: mode_t) -> OsResultStatic<()> {
|
||||
if self.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut arr = cstr_buf::default();
|
||||
arr.push_str(self);
|
||||
let mut off = 1;
|
||||
unsafe {
|
||||
let buf = arr.as_bytes_mut();
|
||||
while let Some(p) = buf[off..].iter().position(|c| *c == b'/') {
|
||||
buf[off + p] = b'\0';
|
||||
if libc::mkdir(buf.as_ptr().cast(), mode) < 0 && *errno() != EEXIST {
|
||||
return Err(io::Error::last_os_error());
|
||||
|
||||
let mut path = cstr::buf::default();
|
||||
let mut components = self.split('/').filter(|s| !s.is_empty());
|
||||
|
||||
if self.starts_with('/') {
|
||||
path.append_path("/");
|
||||
}
|
||||
|
||||
loop {
|
||||
let Some(s) = components.next() else {
|
||||
break;
|
||||
};
|
||||
path.append_path(s);
|
||||
|
||||
unsafe {
|
||||
if libc::mkdir(path.as_ptr(), mode) < 0 && *errno() != EEXIST {
|
||||
return Err(OsError::last_os_error("mkdir", Some(&path), None))?;
|
||||
}
|
||||
buf[off + p] = b'/';
|
||||
off += p + 1;
|
||||
}
|
||||
if libc::mkdir(buf.as_ptr().cast(), mode) < 0 && *errno() != EEXIST {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
*errno() = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Inspired by https://android.googlesource.com/platform/bionic/+/master/libc/bionic/realpath.cpp
|
||||
pub fn realpath(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
let fd = open_fd!(self, O_PATH | O_CLOEXEC)?;
|
||||
pub fn realpath(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
let fd = self.open(O_PATH | O_CLOEXEC)?;
|
||||
let mut st1: libc::stat;
|
||||
let mut st2: libc::stat;
|
||||
let mut skip_check = false;
|
||||
@@ -292,19 +299,19 @@ impl FsPath {
|
||||
fd_path(fd.as_raw_fd(), buf)?;
|
||||
unsafe {
|
||||
st2 = mem::zeroed();
|
||||
libc::stat(buf.as_ptr(), &mut st2).as_os_err()?;
|
||||
libc::stat(buf.as_ptr(), &mut st2).check_os_err("stat", Some(self), None)?;
|
||||
if !skip_check && (st2.st_dev != st1.st_dev || st2.st_ino != st1.st_ino) {
|
||||
*errno() = ENOENT;
|
||||
return Err(io::Error::last_os_error());
|
||||
return Err(OsError::last_os_error("realpath", Some(self), None));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
||||
pub fn get_attr(&self) -> OsResult<FileAttr> {
|
||||
let mut attr = FileAttr::new();
|
||||
unsafe {
|
||||
libc::lstat(self.as_ptr(), &mut attr.st).as_os_err()?;
|
||||
libc::lstat(self.as_ptr(), &mut attr.st).check_os_err("lstat", Some(self), None)?;
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
self.get_secontext(&mut attr.con)?;
|
||||
@@ -312,12 +319,20 @@ impl FsPath {
|
||||
Ok(attr)
|
||||
}
|
||||
|
||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
||||
pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
if !attr.is_symlink() {
|
||||
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).as_os_err()?;
|
||||
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).check_os_err(
|
||||
"chmod",
|
||||
Some(self),
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
libc::lchown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).as_os_err()?;
|
||||
libc::lchown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).check_os_err(
|
||||
"lchown",
|
||||
Some(self),
|
||||
None,
|
||||
)?;
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
if !attr.con.is_empty() {
|
||||
@@ -327,7 +342,7 @@ impl FsPath {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
unsafe {
|
||||
let sz = libc::lgetxattr(
|
||||
self.as_ptr(),
|
||||
@@ -338,7 +353,7 @@ impl FsPath {
|
||||
if sz < 1 {
|
||||
con.clear();
|
||||
if *errno() != libc::ENODATA {
|
||||
return Err(io::Error::last_os_error());
|
||||
return Err(OsError::last_os_error("lgetxattr", Some(self), None));
|
||||
}
|
||||
} else {
|
||||
con.set_len((sz - 1) as usize);
|
||||
@@ -347,7 +362,7 @@ impl FsPath {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||
pub fn set_secontext<'a>(&'a self, con: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::lsetxattr(
|
||||
self.as_ptr(),
|
||||
@@ -356,11 +371,11 @@ impl FsPath {
|
||||
con.len() + 1,
|
||||
0,
|
||||
)
|
||||
.as_os_err()
|
||||
.check_os_err("lsetxattr", Some(self), Some(con))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_to(&self, path: &FsPath) -> io::Result<()> {
|
||||
pub fn copy_to(&self, path: &Utf8CStr) -> OsResultStatic<()> {
|
||||
let attr = self.get_attr()?;
|
||||
if attr.is_dir() {
|
||||
path.mkdir(0o777)?;
|
||||
@@ -375,10 +390,14 @@ impl FsPath {
|
||||
let mut dest = path.create(O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0o777)?;
|
||||
std::io::copy(&mut src, &mut dest)?;
|
||||
} else if attr.is_symlink() {
|
||||
let mut buf = cstr_buf::default();
|
||||
let mut buf = cstr::buf::default();
|
||||
self.read_link(&mut buf)?;
|
||||
unsafe {
|
||||
libc::symlink(buf.as_ptr(), path.as_ptr()).as_os_err()?;
|
||||
libc::symlink(buf.as_ptr(), path.as_ptr()).check_os_err(
|
||||
"symlink",
|
||||
Some(&buf),
|
||||
Some(path),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,7 +405,7 @@ impl FsPath {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn move_to(&self, path: &FsPath) -> io::Result<()> {
|
||||
pub fn move_to(&self, path: &Utf8CStr) -> OsResultStatic<()> {
|
||||
if path.exists() {
|
||||
let attr = path.get_attr()?;
|
||||
if attr.is_dir() {
|
||||
@@ -397,37 +416,59 @@ impl FsPath {
|
||||
path.remove()?;
|
||||
}
|
||||
}
|
||||
self.rename_to(path)
|
||||
self.rename_to(path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn link_to(&self, path: &FsPath) -> io::Result<()> {
|
||||
pub fn parent_dir(&self) -> Option<&str> {
|
||||
Path::new(self.as_str())
|
||||
.parent()
|
||||
.map(Path::as_os_str)
|
||||
// SAFETY: all substring of self is valid UTF-8
|
||||
.map(|s| unsafe { std::str::from_utf8_unchecked(s.as_bytes()) })
|
||||
}
|
||||
|
||||
pub fn file_name(&self) -> Option<&str> {
|
||||
Path::new(self.as_str())
|
||||
.file_name()
|
||||
// SAFETY: all substring of self is valid UTF-8
|
||||
.map(|s| unsafe { std::str::from_utf8_unchecked(s.as_bytes()) })
|
||||
}
|
||||
|
||||
// ln self path
|
||||
pub fn link_to(&self, path: &Utf8CStr) -> OsResultStatic<()> {
|
||||
let attr = self.get_attr()?;
|
||||
if attr.is_dir() {
|
||||
path.mkdir(0o777)?;
|
||||
path.set_attr(&attr)?;
|
||||
let mut src = Directory::open(self)?;
|
||||
let dest = Directory::open(path)?;
|
||||
src.link_into(&dest)
|
||||
Ok(src.link_into(&dest)?)
|
||||
} else {
|
||||
unsafe { libc::link(self.as_ptr(), path.as_ptr()).as_os_err() }
|
||||
unsafe {
|
||||
libc::link(self.as_ptr(), path.as_ptr()).check_os_err(
|
||||
"link",
|
||||
Some(self),
|
||||
Some(path),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symlink_to(&self, path: &FsPath) -> io::Result<()> {
|
||||
unsafe { libc::symlink(self.as_ptr(), path.as_ptr()).as_os_err() }
|
||||
// ln -s target self
|
||||
pub fn create_symlink_to<'a>(&'a self, target: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::symlink(target.as_ptr(), self.as_ptr()).check_os_err(
|
||||
"symlink",
|
||||
Some(target),
|
||||
Some(self),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent(&self, buf: &mut dyn Utf8CStrBuf) -> bool {
|
||||
buf.clear();
|
||||
if let Some(parent) = Path::new(self.as_str()).parent() {
|
||||
let bytes = parent.as_os_str().as_bytes();
|
||||
// SAFETY: all substring of self is valid UTF-8
|
||||
let parent = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||
buf.push_str(parent);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
pub fn mkfifo(&self, mode: mode_t) -> OsResult<()> {
|
||||
unsafe { libc::mkfifo(self.as_ptr(), mode).check_os_err("mkfifo", Some(self), None) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,10 +477,10 @@ impl FsPathFollow {
|
||||
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
|
||||
}
|
||||
|
||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
||||
pub fn get_attr(&self) -> OsResult<FileAttr> {
|
||||
let mut attr = FileAttr::new();
|
||||
unsafe {
|
||||
libc::stat(self.as_ptr(), &mut attr.st).as_os_err()?;
|
||||
libc::stat(self.as_ptr(), &mut attr.st).check_os_err("stat", Some(self), None)?;
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
self.get_secontext(&mut attr.con)?;
|
||||
@@ -447,10 +488,18 @@ impl FsPathFollow {
|
||||
Ok(attr)
|
||||
}
|
||||
|
||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
||||
pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).as_os_err()?;
|
||||
libc::chown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).as_os_err()?;
|
||||
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).check_os_err(
|
||||
"chmod",
|
||||
Some(self),
|
||||
None,
|
||||
)?;
|
||||
libc::chown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).check_os_err(
|
||||
"chown",
|
||||
Some(self),
|
||||
None,
|
||||
)?;
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
if !attr.con.is_empty() {
|
||||
@@ -460,7 +509,7 @@ impl FsPathFollow {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
unsafe {
|
||||
let sz = libc::getxattr(
|
||||
self.as_ptr(),
|
||||
@@ -471,7 +520,7 @@ impl FsPathFollow {
|
||||
if sz < 1 {
|
||||
con.clear();
|
||||
if *errno() != libc::ENODATA {
|
||||
return Err(io::Error::last_os_error());
|
||||
return Err(OsError::last_os_error("getxattr", Some(self), None));
|
||||
}
|
||||
} else {
|
||||
con.set_len((sz - 1) as usize);
|
||||
@@ -480,7 +529,7 @@ impl FsPathFollow {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||
pub fn set_secontext<'a>(&'a self, con: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::setxattr(
|
||||
self.as_ptr(),
|
||||
@@ -489,86 +538,150 @@ impl FsPathFollow {
|
||||
con.len() + 1,
|
||||
0,
|
||||
)
|
||||
.as_os_err()
|
||||
.check_os_err("setxattr", Some(self), Some(con))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
||||
pub trait FsPathBuilder {
|
||||
fn join_path<T: AsRef<str>>(mut self, path: T) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.append_path(path);
|
||||
self
|
||||
}
|
||||
fn join_path_fmt<T: Display>(mut self, name: T) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.append_path_fmt(name);
|
||||
self
|
||||
}
|
||||
fn append_path<T: AsRef<str>>(&mut self, path: T) -> &mut Self;
|
||||
fn append_path_fmt<T: Display>(&mut self, name: T) -> &mut Self;
|
||||
}
|
||||
|
||||
fn append_path_impl(buf: &mut dyn Utf8CStrBuf, path: &str) {
|
||||
if path.starts_with('/') {
|
||||
buf.clear();
|
||||
}
|
||||
if !buf.is_empty() && !buf.ends_with('/') {
|
||||
buf.push_str("/");
|
||||
}
|
||||
buf.push_str(path);
|
||||
}
|
||||
|
||||
impl<S: Utf8CStrBuf + Sized> FsPathBuilder for S {
|
||||
fn append_path<T: AsRef<str>>(&mut self, path: T) -> &mut Self {
|
||||
append_path_impl(self, path.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
fn append_path_fmt<T: Display>(&mut self, name: T) -> &mut Self {
|
||||
self.write_fmt(format_args!("/{}", name)).ok();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FsPathBuilder for dyn Utf8CStrBuf + '_ {
|
||||
fn append_path<T: AsRef<str>>(&mut self, path: T) -> &mut Self {
|
||||
append_path_impl(self, path.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
fn append_path_fmt<T: Display>(&mut self, name: T) -> &mut Self {
|
||||
self.write_fmt(format_args!("/{}", name)).ok();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd_get_attr(fd: RawFd) -> OsResult<'static, FileAttr> {
|
||||
let mut attr = FileAttr::new();
|
||||
unsafe {
|
||||
libc::fstat(fd, &mut attr.st).as_os_err()?;
|
||||
libc::fstat(fd, &mut attr.st).check_os_err("fstat", None, None)?;
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
{
|
||||
let sz = libc::fgetxattr(
|
||||
fd,
|
||||
XATTR_NAME_SELINUX.as_ptr(),
|
||||
attr.con.as_mut_ptr().cast(),
|
||||
attr.con.capacity(),
|
||||
);
|
||||
if sz < 1 {
|
||||
if *errno() != libc::ENODATA {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
} else {
|
||||
attr.con.set_len((sz - 1) as usize);
|
||||
}
|
||||
}
|
||||
fd_get_secontext(fd, &mut attr.con)?;
|
||||
}
|
||||
Ok(attr)
|
||||
}
|
||||
|
||||
pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> io::Result<()> {
|
||||
pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> OsResult<()> {
|
||||
unsafe {
|
||||
libc::fchmod(fd, (attr.st.st_mode & 0o777).as_()).as_os_err()?;
|
||||
libc::fchown(fd, attr.st.st_uid, attr.st.st_gid).as_os_err()?;
|
||||
libc::fchmod(fd, (attr.st.st_mode & 0o777).as_()).check_os_err("fchmod", None, None)?;
|
||||
libc::fchown(fd, attr.st.st_uid, attr.st.st_gid).check_os_err("fchown", None, None)?;
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
if !attr.con.is_empty() {
|
||||
libc::fsetxattr(
|
||||
fd,
|
||||
XATTR_NAME_SELINUX.as_ptr(),
|
||||
attr.con.as_ptr().cast(),
|
||||
attr.con.len() + 1,
|
||||
0,
|
||||
)
|
||||
.as_os_err()?;
|
||||
fd_set_secontext(fd, &attr.con)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clone_attr(a: &FsPath, b: &FsPath) -> io::Result<()> {
|
||||
let attr = a.get_attr()?;
|
||||
b.set_attr(&attr)
|
||||
pub fn fd_get_secontext(fd: RawFd, con: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||
unsafe {
|
||||
let sz = libc::fgetxattr(
|
||||
fd,
|
||||
XATTR_NAME_SELINUX.as_ptr(),
|
||||
con.as_mut_ptr().cast(),
|
||||
con.capacity(),
|
||||
);
|
||||
if sz < 1 {
|
||||
if *errno() != libc::ENODATA {
|
||||
return Err(OsError::last_os_error("fgetxattr", None, None));
|
||||
}
|
||||
} else {
|
||||
con.set_len((sz - 1) as usize);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fclone_attr(a: RawFd, b: RawFd) -> io::Result<()> {
|
||||
pub fn fd_set_secontext(fd: RawFd, con: &Utf8CStr) -> OsResult<()> {
|
||||
unsafe {
|
||||
libc::fsetxattr(
|
||||
fd,
|
||||
XATTR_NAME_SELINUX.as_ptr(),
|
||||
con.as_ptr().cast(),
|
||||
con.len() + 1,
|
||||
0,
|
||||
)
|
||||
.check_os_err("fsetxattr", Some(con), None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone_attr<'a>(a: &'a Utf8CStr, b: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||
let attr = a.get_attr().map_err(|e| e.set_args(Some(a), None))?;
|
||||
b.set_attr(&attr).map_err(|e| e.set_args(Some(b), None))
|
||||
}
|
||||
|
||||
pub fn fclone_attr(a: RawFd, b: RawFd) -> OsResult<'static, ()> {
|
||||
let attr = fd_get_attr(a)?;
|
||||
fd_set_attr(b, &attr)
|
||||
fd_set_attr(b, &attr).map_err(|e| e.set_args(None, None))
|
||||
}
|
||||
|
||||
pub struct MappedFile(&'static mut [u8]);
|
||||
|
||||
impl MappedFile {
|
||||
pub fn open(path: &Utf8CStr) -> io::Result<MappedFile> {
|
||||
pub fn open(path: &Utf8CStr) -> OsResult<MappedFile> {
|
||||
Ok(MappedFile(map_file(path, false)?))
|
||||
}
|
||||
|
||||
pub fn open_rw(path: &Utf8CStr) -> io::Result<MappedFile> {
|
||||
pub fn open_rw(path: &Utf8CStr) -> OsResult<MappedFile> {
|
||||
Ok(MappedFile(map_file(path, true)?))
|
||||
}
|
||||
|
||||
pub fn openat<T: AsFd>(dir: &T, path: &Utf8CStr) -> io::Result<MappedFile> {
|
||||
pub fn openat<'a, T: AsFd>(dir: &T, path: &'a Utf8CStr) -> OsResult<'a, MappedFile> {
|
||||
Ok(MappedFile(map_file_at(dir.as_fd(), path, false)?))
|
||||
}
|
||||
|
||||
pub fn openat_rw<T: AsFd>(dir: &T, path: &Utf8CStr) -> io::Result<MappedFile> {
|
||||
pub fn openat_rw<'a, T: AsFd>(dir: &T, path: &'a Utf8CStr) -> OsResult<'a, MappedFile> {
|
||||
Ok(MappedFile(map_file_at(dir.as_fd(), path, true)?))
|
||||
}
|
||||
|
||||
pub fn create(fd: BorrowedFd, sz: usize, rw: bool) -> io::Result<MappedFile> {
|
||||
pub fn create(fd: BorrowedFd, sz: usize, rw: bool) -> OsResult<MappedFile> {
|
||||
Ok(MappedFile(map_fd(fd, sz, rw)?))
|
||||
}
|
||||
}
|
||||
@@ -599,15 +712,15 @@ unsafe extern "C" {
|
||||
}
|
||||
|
||||
// We mark the returned slice static because it is valid until explicitly unmapped
|
||||
pub(crate) fn map_file(path: &Utf8CStr, rw: bool) -> io::Result<&'static mut [u8]> {
|
||||
pub(crate) fn map_file(path: &Utf8CStr, rw: bool) -> OsResult<&'static mut [u8]> {
|
||||
unsafe { map_file_at(BorrowedFd::borrow_raw(libc::AT_FDCWD), path, rw) }
|
||||
}
|
||||
|
||||
pub(crate) fn map_file_at(
|
||||
pub(crate) fn map_file_at<'a>(
|
||||
dirfd: BorrowedFd,
|
||||
path: &Utf8CStr,
|
||||
path: &'a Utf8CStr,
|
||||
rw: bool,
|
||||
) -> io::Result<&'static mut [u8]> {
|
||||
) -> OsResult<'a, &'static mut [u8]> {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const BLKGETSIZE64: u32 = 0x80081272;
|
||||
|
||||
@@ -617,23 +730,29 @@ pub(crate) fn map_file_at(
|
||||
let flag = if rw { O_RDWR } else { O_RDONLY };
|
||||
let fd = unsafe {
|
||||
OwnedFd::from_raw_fd(
|
||||
libc::openat(dirfd.as_raw_fd(), path.as_ptr(), flag | O_CLOEXEC).check_os_err()?,
|
||||
libc::openat(dirfd.as_raw_fd(), path.as_ptr(), flag | O_CLOEXEC).as_os_result(
|
||||
"openat",
|
||||
Some(path),
|
||||
None,
|
||||
)?,
|
||||
)
|
||||
};
|
||||
|
||||
let attr = fd_get_attr(fd.as_raw_fd())?;
|
||||
let sz = if attr.is_block_device() {
|
||||
let mut sz = 0_u64;
|
||||
unsafe { ioctl(fd.as_raw_fd(), BLKGETSIZE64, &mut sz) }.as_os_err()?;
|
||||
unsafe {
|
||||
ioctl(fd.as_raw_fd(), BLKGETSIZE64, &mut sz).check_os_err("ioctl", Some(path), None)?;
|
||||
}
|
||||
sz
|
||||
} else {
|
||||
attr.st.st_size as u64
|
||||
};
|
||||
|
||||
map_fd(fd.as_fd(), sz as usize, rw)
|
||||
map_fd(fd.as_fd(), sz as usize, rw).map_err(|e| e.set_args(Some(path), None))
|
||||
}
|
||||
|
||||
pub(crate) fn map_fd(fd: BorrowedFd, sz: usize, rw: bool) -> io::Result<&'static mut [u8]> {
|
||||
pub(crate) fn map_fd(fd: BorrowedFd, sz: usize, rw: bool) -> OsResult<'static, &'static mut [u8]> {
|
||||
let flag = if rw {
|
||||
libc::MAP_SHARED
|
||||
} else {
|
||||
@@ -649,7 +768,7 @@ pub(crate) fn map_fd(fd: BorrowedFd, sz: usize, rw: bool) -> io::Result<&'static
|
||||
0,
|
||||
);
|
||||
if ptr == libc::MAP_FAILED {
|
||||
return Err(io::Error::last_os_error());
|
||||
return Err(OsError::last_os_error("mmap", None, None));
|
||||
}
|
||||
Ok(slice::from_raw_parts_mut(ptr.cast(), sz))
|
||||
}
|
||||
@@ -724,8 +843,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);
|
||||
if let Ok(fd) = open_fd!(Utf8CStr::from_string(&mut path), O_RDONLY | O_CLOEXEC) {
|
||||
let file = File::from(fd);
|
||||
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)
|
||||
.map(|info| res.push(info))
|
||||
|
||||
@@ -6,7 +6,9 @@ pub use const_format;
|
||||
pub use libc;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
pub use cstr::*;
|
||||
pub use cstr::{
|
||||
FsPathFollow, StrErr, Utf8CStr, Utf8CStrBuf, Utf8CStrBufArr, Utf8CStrBufRef, Utf8CString,
|
||||
};
|
||||
use cxx_extern::*;
|
||||
pub use dir::*;
|
||||
pub use ffi::fork_dont_care;
|
||||
@@ -15,12 +17,13 @@ pub use logging::*;
|
||||
pub use misc::*;
|
||||
pub use result::*;
|
||||
|
||||
mod cstr;
|
||||
pub mod cstr;
|
||||
mod cxx_extern;
|
||||
mod dir;
|
||||
mod files;
|
||||
mod logging;
|
||||
mod misc;
|
||||
mod mount;
|
||||
mod result;
|
||||
mod xwrap;
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Arguments;
|
||||
use std::io::{stderr, stdout, Write};
|
||||
use std::io::{Write, stderr, stdout};
|
||||
use std::process::exit;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::ffi::LogLevelCxx;
|
||||
use crate::{cstr_buf, Utf8CStr};
|
||||
use crate::{Utf8CStr, cstr};
|
||||
|
||||
// Ugly hack to avoid using enum
|
||||
#[allow(non_snake_case, non_upper_case_globals)]
|
||||
@@ -96,7 +96,7 @@ pub fn log_from_cxx(level: LogLevelCxx, msg: &Utf8CStr) {
|
||||
|
||||
pub fn log_with_formatter<F: FnOnce(Formatter) -> fmt::Result>(level: LogLevel, f: F) {
|
||||
log_with_writer(level, |write| {
|
||||
let mut buf = cstr_buf::default();
|
||||
let mut buf = cstr::buf::default();
|
||||
f(&mut buf).ok();
|
||||
write(level, &buf);
|
||||
});
|
||||
|
||||
@@ -186,27 +186,6 @@ int parse_int(string_view s) {
|
||||
return parse_num<int, 10>(s);
|
||||
}
|
||||
|
||||
uint64_t parse_uint64_hex(string_view s) {
|
||||
return parse_num<uint64_t, 16>(s);
|
||||
}
|
||||
|
||||
uint32_t binary_gcd(uint32_t u, uint32_t v) {
|
||||
if (u == 0) return v;
|
||||
if (v == 0) return u;
|
||||
auto shift = __builtin_ctz(u | v);
|
||||
u >>= __builtin_ctz(u);
|
||||
do {
|
||||
v >>= __builtin_ctz(v);
|
||||
if (u > v) {
|
||||
auto t = v;
|
||||
v = u;
|
||||
u = t;
|
||||
}
|
||||
v -= u;
|
||||
} while (v != 0);
|
||||
return u << shift;
|
||||
}
|
||||
|
||||
int switch_mnt_ns(int pid) {
|
||||
int ret = -1;
|
||||
int fd = syscall(__NR_pidfd_open, pid, 0);
|
||||
@@ -255,10 +234,6 @@ vector<string> split(string_view s, string_view delims) {
|
||||
return split_impl<string>(s, delims);
|
||||
}
|
||||
|
||||
vector<string_view> split_view(string_view s, string_view delims) {
|
||||
return split_impl<string_view>(s, delims);
|
||||
}
|
||||
|
||||
#undef vsnprintf
|
||||
int vssprintf(char *dest, size_t size, const char *fmt, va_list ap) {
|
||||
if (size > 0) {
|
||||
|
||||
@@ -71,57 +71,6 @@ static inline void default_new(T *&p) { p = new T(); }
|
||||
template<class T>
|
||||
static inline void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
|
||||
|
||||
template<typename T, typename Impl>
|
||||
class stateless_allocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
T *allocate(size_t num) { return static_cast<T*>(Impl::allocate(sizeof(T) * num)); }
|
||||
void deallocate(T *ptr, size_t num) { Impl::deallocate(ptr, sizeof(T) * num); }
|
||||
stateless_allocator() = default;
|
||||
stateless_allocator(const stateless_allocator&) = default;
|
||||
stateless_allocator(stateless_allocator&&) = default;
|
||||
template <typename U>
|
||||
stateless_allocator(const stateless_allocator<U, Impl>&) {}
|
||||
bool operator==(const stateless_allocator&) { return true; }
|
||||
bool operator!=(const stateless_allocator&) { return false; }
|
||||
};
|
||||
|
||||
class dynamic_bitset_impl {
|
||||
public:
|
||||
using slot_type = unsigned long;
|
||||
constexpr static int slot_size = sizeof(slot_type) * 8;
|
||||
using slot_bits = std::bitset<slot_size>;
|
||||
|
||||
size_t slots() const { return slot_list.size(); }
|
||||
slot_type get_slot(size_t slot) const {
|
||||
return slot_list.size() > slot ? slot_list[slot].to_ulong() : 0ul;
|
||||
}
|
||||
void emplace_back(slot_type l) {
|
||||
slot_list.emplace_back(l);
|
||||
}
|
||||
protected:
|
||||
slot_bits::reference get(size_t pos) {
|
||||
size_t slot = pos / slot_size;
|
||||
size_t index = pos % slot_size;
|
||||
if (slot_list.size() <= slot) {
|
||||
slot_list.resize(slot + 1);
|
||||
}
|
||||
return slot_list[slot][index];
|
||||
}
|
||||
bool get(size_t pos) const {
|
||||
size_t slot = pos / slot_size;
|
||||
size_t index = pos % slot_size;
|
||||
return slot_list.size() > slot && slot_list[slot][index];
|
||||
}
|
||||
private:
|
||||
std::vector<slot_bits> slot_list;
|
||||
};
|
||||
|
||||
struct dynamic_bitset : public dynamic_bitset_impl {
|
||||
slot_bits::reference operator[] (size_t pos) { return get(pos); }
|
||||
bool operator[] (size_t pos) const { return get(pos); }
|
||||
};
|
||||
|
||||
struct StringCmp {
|
||||
using is_transparent = void;
|
||||
bool operator()(std::string_view a, std::string_view b) const { return a < b; }
|
||||
@@ -198,13 +147,6 @@ struct byte_data : public byte_view {
|
||||
rust::Vec<size_t> patch(byte_view from, byte_view to);
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
struct byte_array : public byte_data {
|
||||
byte_array() : byte_data(arr, N), arr{0} {}
|
||||
private:
|
||||
uint8_t arr[N];
|
||||
};
|
||||
|
||||
class byte_stream;
|
||||
|
||||
struct heap_data : public byte_data {
|
||||
@@ -238,7 +180,6 @@ rust::Vec<size_t> mut_u8_patch(
|
||||
rust::Slice<const uint8_t> from,
|
||||
rust::Slice<const uint8_t> to);
|
||||
|
||||
uint64_t parse_uint64_hex(std::string_view s);
|
||||
int parse_int(std::string_view s);
|
||||
|
||||
using thread_entry = void *(*)(void *);
|
||||
@@ -270,11 +211,9 @@ int fork_dont_care();
|
||||
int fork_no_orphan();
|
||||
void init_argv0(int argc, char **argv);
|
||||
void set_nice_name(const char *name);
|
||||
uint32_t binary_gcd(uint32_t u, uint32_t v);
|
||||
int switch_mnt_ns(int pid);
|
||||
std::string &replace_all(std::string &str, std::string_view from, std::string_view to);
|
||||
std::vector<std::string> split(std::string_view s, std::string_view delims);
|
||||
std::vector<std::string_view> split_view(std::string_view, std::string_view delims);
|
||||
|
||||
// Similar to vsnprintf, but the return value is the written number of bytes
|
||||
__printflike(3, 0) int vssprintf(char *dest, size_t size, const char *fmt, va_list ap);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::{ffi, StrErr, Utf8CStr};
|
||||
use crate::{StrErr, Utf8CStr, 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::atomic::{AtomicPtr, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, io, slice, str};
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use std::{fmt, slice, str};
|
||||
|
||||
pub fn errno() -> &'static mut i32 {
|
||||
unsafe { &mut *libc::__errno() }
|
||||
@@ -37,52 +37,6 @@ pub unsafe fn slice_from_ptr_mut<'a, T>(buf: *mut T, len: usize) -> &'a mut [T]
|
||||
}
|
||||
}
|
||||
|
||||
// Check libc return value and map to Result
|
||||
pub trait LibcReturn
|
||||
where
|
||||
Self: Copy,
|
||||
{
|
||||
fn is_error(&self) -> bool;
|
||||
fn check_os_err(self) -> io::Result<Self> {
|
||||
if self.is_error() {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
fn as_os_err(self) -> io::Result<()> {
|
||||
self.check_os_err()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_libc_return {
|
||||
($($t:ty)*) => ($(
|
||||
impl LibcReturn for $t {
|
||||
#[inline]
|
||||
fn is_error(&self) -> bool {
|
||||
*self < 0
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
impl_libc_return! { i8 i16 i32 i64 isize }
|
||||
|
||||
impl<T> LibcReturn for *const T {
|
||||
#[inline]
|
||||
fn is_error(&self) -> bool {
|
||||
self.is_null()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LibcReturn for *mut T {
|
||||
#[inline]
|
||||
fn is_error(&self) -> bool {
|
||||
self.is_null()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BytesExt {
|
||||
fn find(&self, needle: &[u8]) -> Option<usize>;
|
||||
fn contains(&self, needle: &[u8]) -> bool {
|
||||
|
||||
90
native/src/base/mount.rs
Normal file
90
native/src/base/mount.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use crate::{LibcReturn, OsResult, Utf8CStr};
|
||||
use libc::c_ulong;
|
||||
use std::ptr;
|
||||
|
||||
impl Utf8CStr {
|
||||
pub fn bind_mount_to<'a>(&'a self, path: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
self.as_ptr(),
|
||||
path.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_BIND,
|
||||
ptr::null(),
|
||||
)
|
||||
.check_os_err("bind_mount", Some(self), Some(path))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remount_mount_point_flags(&self, flags: c_ulong) -> OsResult<()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
self.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_BIND | libc::MS_REMOUNT | flags,
|
||||
ptr::null(),
|
||||
)
|
||||
.check_os_err("remount", Some(self), None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remount_mount_flags(&self, flags: c_ulong) -> OsResult<()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
self.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_REMOUNT | flags,
|
||||
ptr::null(),
|
||||
)
|
||||
.check_os_err("remount", Some(self), None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remount_with_data(&self, data: &Utf8CStr) -> OsResult<()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
self.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_REMOUNT,
|
||||
data.as_ptr().cast(),
|
||||
)
|
||||
.check_os_err("remount", Some(self), None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_mount_to<'a>(&'a self, path: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
self.as_ptr(),
|
||||
path.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_MOVE,
|
||||
ptr::null(),
|
||||
)
|
||||
.check_os_err("move_mount", Some(self), Some(path))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmount(&self) -> OsResult<()> {
|
||||
unsafe {
|
||||
libc::umount2(self.as_ptr(), libc::MNT_DETACH).check_os_err("unmount", Some(self), None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_mount_private(&self, recursive: bool) -> OsResult<()> {
|
||||
let flag = if recursive { libc::MS_REC } else { 0 };
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
self.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_PRIVATE | flag,
|
||||
ptr::null(),
|
||||
)
|
||||
.check_os_err("set_mount_private", Some(self), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
use std::fmt;
|
||||
use crate::logging::Formatter;
|
||||
use crate::{LogLevel, errno, log_with_args, log_with_formatter};
|
||||
use std::fmt::Display;
|
||||
use std::panic::Location;
|
||||
|
||||
use crate::logging::Formatter;
|
||||
use crate::{log_with_args, log_with_formatter, LogLevel};
|
||||
use std::ptr::NonNull;
|
||||
use std::{fmt, io};
|
||||
use thiserror::Error;
|
||||
|
||||
// Error handling throughout the Rust codebase in Magisk:
|
||||
//
|
||||
@@ -61,7 +62,6 @@ pub trait ResultExt<T> {
|
||||
// Internal C++ bridging logging routines
|
||||
pub(crate) trait CxxResultExt<T> {
|
||||
fn log_cxx(self) -> LoggedResult<T>;
|
||||
fn log_cxx_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T>;
|
||||
}
|
||||
|
||||
trait Loggable<T> {
|
||||
@@ -78,10 +78,6 @@ impl<T, R: Loggable<T>> CxxResultExt<T> for R {
|
||||
fn log_cxx(self) -> LoggedResult<T> {
|
||||
self.do_log(LogLevel::ErrorCxx, None)
|
||||
}
|
||||
|
||||
fn log_cxx_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
||||
self.do_log_msg(LogLevel::ErrorCxx, None, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R: Loggable<T>> ResultExt<T> for R {
|
||||
@@ -205,3 +201,205 @@ impl<T: Display> From<T> for LoggedError {
|
||||
LoggedError::default()
|
||||
}
|
||||
}
|
||||
|
||||
// Check libc return value and map to Result
|
||||
pub trait LibcReturn
|
||||
where
|
||||
Self: Copy,
|
||||
{
|
||||
type Value;
|
||||
|
||||
fn is_error(&self) -> bool;
|
||||
fn map_val(self) -> Self::Value;
|
||||
|
||||
fn as_os_result<'a>(
|
||||
self,
|
||||
name: &'static str,
|
||||
arg1: Option<&'a str>,
|
||||
arg2: Option<&'a str>,
|
||||
) -> OsResult<'a, Self::Value> {
|
||||
if self.is_error() {
|
||||
Err(OsError::last_os_error(name, arg1, arg2))
|
||||
} else {
|
||||
Ok(self.map_val())
|
||||
}
|
||||
}
|
||||
|
||||
fn check_os_err<'a>(
|
||||
self,
|
||||
name: &'static str,
|
||||
arg1: Option<&'a str>,
|
||||
arg2: Option<&'a str>,
|
||||
) -> OsResult<'a, ()> {
|
||||
if self.is_error() {
|
||||
Err(OsError::last_os_error(name, arg1, arg2))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn check_io_err(self) -> io::Result<()> {
|
||||
if self.is_error() {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_libc_return {
|
||||
($($t:ty)*) => ($(
|
||||
impl LibcReturn for $t {
|
||||
type Value = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_error(&self) -> bool {
|
||||
*self < 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn map_val(self) -> Self::Value {
|
||||
self
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
impl_libc_return! { i8 i16 i32 i64 isize }
|
||||
|
||||
impl<T> LibcReturn for *mut T {
|
||||
type Value = NonNull<T>;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_error(&self) -> bool {
|
||||
self.is_null()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn map_val(self) -> NonNull<T> {
|
||||
// SAFETY: pointer is null checked by is_error
|
||||
unsafe { NonNull::new_unchecked(self.cast()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum OwnableStr<'a> {
|
||||
None,
|
||||
Borrowed(&'a str),
|
||||
Owned(Box<str>),
|
||||
}
|
||||
|
||||
impl OwnableStr<'_> {
|
||||
fn into_owned(self) -> OwnableStr<'static> {
|
||||
match self {
|
||||
OwnableStr::None => OwnableStr::None,
|
||||
OwnableStr::Borrowed(s) => OwnableStr::Owned(Box::from(s)),
|
||||
OwnableStr::Owned(s) => OwnableStr::Owned(s),
|
||||
}
|
||||
}
|
||||
|
||||
fn ok(&self) -> Option<&str> {
|
||||
match self {
|
||||
OwnableStr::None => None,
|
||||
OwnableStr::Borrowed(s) => Some(*s),
|
||||
OwnableStr::Owned(s) => Some(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Option<&'a str>> for OwnableStr<'a> {
|
||||
fn from(value: Option<&'a str>) -> Self {
|
||||
value.map(OwnableStr::Borrowed).unwrap_or(OwnableStr::None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OsError<'a> {
|
||||
code: i32,
|
||||
name: &'static str,
|
||||
arg1: OwnableStr<'a>,
|
||||
arg2: OwnableStr<'a>,
|
||||
}
|
||||
|
||||
impl OsError<'_> {
|
||||
pub fn with_os_error<'a>(
|
||||
code: i32,
|
||||
name: &'static str,
|
||||
arg1: Option<&'a str>,
|
||||
arg2: Option<&'a str>,
|
||||
) -> OsError<'a> {
|
||||
OsError {
|
||||
code,
|
||||
name,
|
||||
arg1: OwnableStr::from(arg1),
|
||||
arg2: OwnableStr::from(arg2),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_os_error<'a>(
|
||||
name: &'static str,
|
||||
arg1: Option<&'a str>,
|
||||
arg2: Option<&'a str>,
|
||||
) -> OsError<'a> {
|
||||
Self::with_os_error(*errno(), name, arg1, arg2)
|
||||
}
|
||||
|
||||
pub fn set_args<'a>(self, arg1: Option<&'a str>, arg2: Option<&'a str>) -> OsError<'a> {
|
||||
Self::with_os_error(self.code, self.name, arg1, arg2)
|
||||
}
|
||||
|
||||
pub fn into_owned(self) -> OsError<'static> {
|
||||
OsError {
|
||||
code: *errno(),
|
||||
name: self.name,
|
||||
arg1: self.arg1.into_owned(),
|
||||
arg2: self.arg2.into_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_io_error(&self) -> io::Error {
|
||||
io::Error::from_raw_os_error(self.code)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
} else {
|
||||
match (self.arg1.ok(), self.arg2.ok()) {
|
||||
(Some(arg1), Some(arg2)) => {
|
||||
write!(f, "{} '{}' '{}': {:#}", self.name, arg1, arg2, error)
|
||||
}
|
||||
(Some(arg1), None) => {
|
||||
write!(f, "{} '{}': {:#}", self.name, arg1, error)
|
||||
}
|
||||
_ => {
|
||||
write!(f, "{}: {:#}", self.name, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for OsError<'_> {}
|
||||
|
||||
pub type OsResult<'a, T> = Result<T, OsError<'a>>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OsErrorStatic {
|
||||
#[error(transparent)]
|
||||
Os(OsError<'static>),
|
||||
#[error(transparent)]
|
||||
Io(#[from] io::Error),
|
||||
}
|
||||
|
||||
// Convert non-static OsError to static
|
||||
impl<'a> From<OsError<'a>> for OsErrorStatic {
|
||||
fn from(value: OsError<'a>) -> Self {
|
||||
OsErrorStatic::Os(value.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
pub type OsResultStatic<T> = Result<T, OsErrorStatic>;
|
||||
|
||||
@@ -25,7 +25,6 @@ int xsocket(int domain, int type, int protocol);
|
||||
int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int xlisten(int sockfd, int backlog);
|
||||
int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
|
||||
int xaccess(const char *path, int mode);
|
||||
int xstat(const char *pathname, struct stat *buf);
|
||||
int xfstat(int fd, struct stat *buf);
|
||||
int xdup(int fd);
|
||||
|
||||
@@ -1,62 +1,26 @@
|
||||
// Functions in this file are only for exporting to C++, DO NOT USE IN RUST
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use crate::cxx_extern::readlinkat;
|
||||
use crate::{
|
||||
BorrowedDirectory, CxxResultExt, LibcReturn, Utf8CStr, cstr, slice_from_ptr, slice_from_ptr_mut,
|
||||
};
|
||||
use libc::{
|
||||
c_char, c_uint, c_ulong, c_void, dev_t, mode_t, nfds_t, off_t, pollfd, sockaddr, socklen_t,
|
||||
ssize_t,
|
||||
};
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::os::fd::FromRawFd;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use crate::cxx_extern::readlinkat_for_cxx;
|
||||
use crate::{CxxResultExt, FsPath, Utf8CStr, Utf8CStrBufRef, errno, raw_cstr};
|
||||
|
||||
fn ptr_to_str<'a, T>(ptr: *const T) -> &'a str {
|
||||
fn ptr_to_str<'a>(ptr: *const c_char) -> Option<&'a str> {
|
||||
if ptr.is_null() {
|
||||
"(null)"
|
||||
None
|
||||
} else {
|
||||
unsafe { CStr::from_ptr(ptr.cast()) }.to_str().unwrap_or("")
|
||||
}
|
||||
}
|
||||
|
||||
fn error_str() -> &'static str {
|
||||
unsafe { ptr_to_str(libc::strerror(*errno())) }
|
||||
}
|
||||
|
||||
macro_rules! error_cxx {
|
||||
($($args:tt)+) => {
|
||||
($crate::log_with_args($crate::LogLevel::ErrorCxx, format_args_nl!($($args)+)))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! perror {
|
||||
($fmt:expr) => {
|
||||
$crate::log_with_formatter($crate::LogLevel::ErrorCxx, |w| {
|
||||
w.write_str($fmt)?;
|
||||
w.write_fmt(format_args_nl!(" failed with {}: {}", $crate::errno(), error_str()))
|
||||
})
|
||||
};
|
||||
($fmt:expr, $($args:tt)*) => {
|
||||
$crate::log_with_formatter($crate::LogLevel::ErrorCxx, |w| {
|
||||
w.write_fmt(format_args!($fmt, $($args)*))?;
|
||||
w.write_fmt(format_args_nl!(" failed with {}: {}", $crate::errno(), error_str()))
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
mod c_export {
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use crate::{slice_from_ptr, slice_from_ptr_mut};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize {
|
||||
unsafe { super::xwrite(fd, slice_from_ptr(buf, bufsz)) }
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xxread(fd: RawFd, buf: *mut u8, bufsz: usize) -> isize {
|
||||
unsafe { super::xxread(fd, slice_from_ptr_mut(buf, bufsz)) }
|
||||
unsafe { CStr::from_ptr(ptr) }.to_str().ok()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +28,10 @@ mod c_export {
|
||||
unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => {
|
||||
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
||||
FsPath::from(p)
|
||||
.realpath(&mut buf)
|
||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("realpath {} failed", p)))
|
||||
Ok(path) => {
|
||||
let mut buf = cstr::buf::wrap_ptr(buf, bufsz);
|
||||
path.realpath(&mut buf)
|
||||
.log_cxx()
|
||||
.map_or(-1, |_| buf.len() as isize)
|
||||
}
|
||||
Err(_) => -1,
|
||||
@@ -80,11 +43,10 @@ unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize)
|
||||
unsafe extern "C" fn xreadlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => {
|
||||
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
||||
FsPath::from(p)
|
||||
.read_link(&mut buf)
|
||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("readlink {} failed", p)))
|
||||
Ok(path) => {
|
||||
let mut buf = cstr::buf::wrap_ptr(buf, bufsz);
|
||||
path.read_link(&mut buf)
|
||||
.log_cxx()
|
||||
.map_or(-1, |_| buf.len() as isize)
|
||||
}
|
||||
Err(_) => -1,
|
||||
@@ -100,244 +62,176 @@ unsafe extern "C" fn xreadlinkat(
|
||||
bufsz: usize,
|
||||
) -> isize {
|
||||
unsafe {
|
||||
let r = readlinkat_for_cxx(dirfd, path, buf, bufsz);
|
||||
if r < 0 {
|
||||
perror!("readlinkat {}", ptr_to_str(path))
|
||||
}
|
||||
r
|
||||
readlinkat(dirfd, path, buf, bufsz)
|
||||
.as_os_result("readlinkat", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xfopen(path: *const c_char, mode: *const c_char) -> *mut libc::FILE {
|
||||
unsafe {
|
||||
let fp = libc::fopen(path, mode);
|
||||
if fp.is_null() {
|
||||
perror!("fopen {}", ptr_to_str(path));
|
||||
}
|
||||
fp
|
||||
libc::fopen(path, mode)
|
||||
.as_os_result("fopen", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.map_or(ptr::null_mut(), NonNull::as_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xfdopen(fd: RawFd, mode: *const c_char) -> *mut libc::FILE {
|
||||
unsafe {
|
||||
let fp = libc::fdopen(fd, mode);
|
||||
if fp.is_null() {
|
||||
perror!("fdopen");
|
||||
}
|
||||
fp
|
||||
libc::fdopen(fd, mode)
|
||||
.as_os_result("fdopen", None, None)
|
||||
.log_cxx()
|
||||
.map_or(ptr::null_mut(), NonNull::as_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xopen(path: *const c_char, flags: i32, mode: mode_t) -> RawFd {
|
||||
unsafe {
|
||||
let r = libc::open(path, flags, mode as c_uint);
|
||||
if r < 0 {
|
||||
perror!("open {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
libc::open(path, flags, mode as c_uint)
|
||||
.as_os_result("open", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xopenat(dirfd: RawFd, path: *const c_char, flags: i32, mode: mode_t) -> RawFd {
|
||||
unsafe {
|
||||
let r = libc::openat(dirfd, path, flags, mode as c_uint);
|
||||
if r < 0 {
|
||||
perror!("openat {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
libc::openat(dirfd, path, flags, mode as c_uint)
|
||||
.as_os_result("openat", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
// Fully write data slice
|
||||
fn xwrite(fd: RawFd, data: &[u8]) -> isize {
|
||||
unsafe {
|
||||
let mut write_sz: usize = 0;
|
||||
let mut r: ssize_t;
|
||||
let mut remain: &[u8] = data;
|
||||
loop {
|
||||
r = libc::write(fd, remain.as_ptr().cast(), remain.len());
|
||||
if r < 0 {
|
||||
if *errno() == libc::EINTR {
|
||||
continue;
|
||||
}
|
||||
perror!("write");
|
||||
return r;
|
||||
}
|
||||
let r = r as usize;
|
||||
write_sz += r;
|
||||
remain = &remain[r..];
|
||||
if r == 0 || remain.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !remain.is_empty() {
|
||||
error_cxx!("write ({} != {})", write_sz, data.len())
|
||||
}
|
||||
write_sz as isize
|
||||
}
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize {
|
||||
let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) };
|
||||
let data = unsafe { slice_from_ptr(buf, bufsz) };
|
||||
file.write_all(data)
|
||||
.log_cxx()
|
||||
.map_or(-1, |_| data.len() as isize)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xread(fd: RawFd, buf: *mut c_void, bufsz: usize) -> isize {
|
||||
unsafe {
|
||||
let r = libc::read(fd, buf, bufsz);
|
||||
if r < 0 {
|
||||
perror!("read");
|
||||
}
|
||||
r
|
||||
libc::read(fd, buf, bufsz)
|
||||
.as_os_result("read", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
// Fully read size of data slice
|
||||
fn xxread(fd: RawFd, data: &mut [u8]) -> isize {
|
||||
unsafe {
|
||||
let mut read_sz: usize = 0;
|
||||
let mut r: ssize_t;
|
||||
let mut remain: &mut [u8] = data;
|
||||
loop {
|
||||
r = libc::read(fd, remain.as_mut_ptr().cast(), remain.len());
|
||||
if r < 0 {
|
||||
if *errno() == libc::EINTR {
|
||||
continue;
|
||||
}
|
||||
perror!("read");
|
||||
return r;
|
||||
}
|
||||
let r = r as usize;
|
||||
read_sz += r;
|
||||
remain = &mut remain[r..];
|
||||
if r == 0 || remain.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !remain.is_empty() {
|
||||
error_cxx!("read ({} != {})", read_sz, data.len())
|
||||
}
|
||||
read_sz as isize
|
||||
}
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xxread(fd: RawFd, buf: *mut u8, bufsz: usize) -> isize {
|
||||
let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) };
|
||||
let data = unsafe { slice_from_ptr_mut(buf, bufsz) };
|
||||
file.read_exact(data)
|
||||
.log_cxx()
|
||||
.map_or(-1, |_| data.len() as isize)
|
||||
}
|
||||
|
||||
pub(crate) fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::pipe2(fds.as_mut_ptr(), flags);
|
||||
if r < 0 {
|
||||
perror!("pipe2");
|
||||
}
|
||||
r
|
||||
libc::pipe2(fds.as_mut_ptr(), flags)
|
||||
.as_os_result("pipe2", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xsetns(fd: RawFd, nstype: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::setns(fd, nstype);
|
||||
if r < 0 {
|
||||
perror!("setns");
|
||||
}
|
||||
r
|
||||
libc::setns(fd, nstype)
|
||||
.as_os_result("setns", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xunshare(flags: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::unshare(flags);
|
||||
if r < 0 {
|
||||
perror!("unshare");
|
||||
}
|
||||
r
|
||||
libc::unshare(flags)
|
||||
.as_os_result("unshare", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xopendir(path: *const c_char) -> *mut libc::DIR {
|
||||
unsafe {
|
||||
let dp = libc::opendir(path);
|
||||
if dp.is_null() {
|
||||
perror!("opendir {}", ptr_to_str(path));
|
||||
}
|
||||
dp
|
||||
libc::opendir(path)
|
||||
.as_os_result("opendir", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.map_or(ptr::null_mut(), NonNull::as_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xfdopendir(fd: RawFd) -> *mut libc::DIR {
|
||||
unsafe {
|
||||
let dp = libc::fdopendir(fd);
|
||||
if dp.is_null() {
|
||||
perror!("fdopendir");
|
||||
}
|
||||
dp
|
||||
libc::fdopendir(fd)
|
||||
.as_os_result("fdopendir", None, None)
|
||||
.log_cxx()
|
||||
.map_or(ptr::null_mut(), NonNull::as_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xreaddir(dirp: *mut libc::DIR) -> *mut libc::dirent {
|
||||
unsafe {
|
||||
*errno() = 0;
|
||||
loop {
|
||||
let e = libc::readdir(dirp);
|
||||
if e.is_null() {
|
||||
if *errno() != 0 {
|
||||
perror!("readdir")
|
||||
}
|
||||
} else {
|
||||
// Filter out . and ..
|
||||
let s = (*e).d_name.as_ptr();
|
||||
if libc::strcmp(s, raw_cstr!(".")) == 0 || libc::strcmp(s, raw_cstr!("..")) == 0 {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
return e;
|
||||
}
|
||||
}
|
||||
unsafe extern "C" fn xreaddir(mut dir: BorrowedDirectory) -> *mut libc::dirent {
|
||||
dir.read()
|
||||
.log_cxx()
|
||||
.ok()
|
||||
.flatten()
|
||||
.map_or(ptr::null_mut(), |entry| entry.as_ptr())
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xsetsid() -> i32 {
|
||||
unsafe {
|
||||
let r = libc::setsid();
|
||||
if r < 0 {
|
||||
perror!("setsid");
|
||||
}
|
||||
r
|
||||
libc::setsid()
|
||||
.as_os_result("setsid", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xsocket(domain: i32, ty: i32, protocol: i32) -> RawFd {
|
||||
unsafe {
|
||||
let fd = libc::socket(domain, ty, protocol);
|
||||
if fd < 0 {
|
||||
perror!("socket");
|
||||
}
|
||||
fd
|
||||
libc::socket(domain, ty, protocol)
|
||||
.as_os_result("socket", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xbind(socket: i32, address: *const sockaddr, len: socklen_t) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::bind(socket, address, len);
|
||||
if r < 0 {
|
||||
perror!("bind");
|
||||
}
|
||||
r
|
||||
libc::bind(socket, address, len)
|
||||
.as_os_result("bind", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xlisten(socket: i32, backlog: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::listen(socket, backlog);
|
||||
if r < 0 {
|
||||
perror!("listen");
|
||||
}
|
||||
r
|
||||
libc::listen(socket, backlog)
|
||||
.as_os_result("listen", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,77 +243,60 @@ unsafe extern "C" fn xaccept4(
|
||||
flg: i32,
|
||||
) -> RawFd {
|
||||
unsafe {
|
||||
let fd = libc::accept4(sockfd, addr, len, flg);
|
||||
if fd < 0 {
|
||||
perror!("accept4");
|
||||
}
|
||||
fd
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xaccess(path: *const c_char, mode: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::access(path, mode);
|
||||
if r < 0 {
|
||||
perror!("access {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
libc::accept4(sockfd, addr, len, flg)
|
||||
.as_os_result("accept4", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xstat(path: *const c_char, buf: *mut libc::stat) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::stat(path, buf);
|
||||
if r < 0 {
|
||||
perror!("stat {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
libc::stat(path, buf)
|
||||
.as_os_result("stat", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xfstat(fd: RawFd, buf: *mut libc::stat) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::fstat(fd, buf);
|
||||
if r < 0 {
|
||||
perror!("fstat");
|
||||
}
|
||||
r
|
||||
libc::fstat(fd, buf)
|
||||
.as_os_result("fstat", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xdup(oldfd: RawFd) -> RawFd {
|
||||
unsafe {
|
||||
let fd = libc::dup(oldfd);
|
||||
if fd < 0 {
|
||||
perror!("dup");
|
||||
}
|
||||
fd
|
||||
libc::dup(oldfd)
|
||||
.as_os_result("dup", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xdup2(oldfd: RawFd, newfd: RawFd) -> RawFd {
|
||||
unsafe {
|
||||
let fd = libc::dup2(oldfd, newfd);
|
||||
if fd < 0 {
|
||||
perror!("dup2");
|
||||
}
|
||||
fd
|
||||
libc::dup2(oldfd, newfd)
|
||||
.as_os_result("dup2", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xsymlink(target: *const c_char, linkpath: *const c_char) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::symlink(target, linkpath);
|
||||
if r < 0 {
|
||||
perror!("symlink {} -> {}", ptr_to_str(target), ptr_to_str(linkpath));
|
||||
}
|
||||
r
|
||||
libc::symlink(target, linkpath)
|
||||
.as_os_result("symlink", ptr_to_str(target), ptr_to_str(linkpath))
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,44 +309,40 @@ unsafe extern "C" fn xmount(
|
||||
data: *const c_void,
|
||||
) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::mount(src, target, fstype, flags, data);
|
||||
if r < 0 {
|
||||
perror!("mount {} -> {}", ptr_to_str(src), ptr_to_str(target));
|
||||
}
|
||||
r
|
||||
libc::mount(src, target, fstype, flags, data)
|
||||
.as_os_result("mount", ptr_to_str(src), ptr_to_str(target))
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xumount2(target: *const c_char, flags: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::umount2(target, flags);
|
||||
if r < 0 {
|
||||
perror!("umount2 {}", ptr_to_str(target));
|
||||
}
|
||||
r
|
||||
libc::umount2(target, flags)
|
||||
.as_os_result("umount2", ptr_to_str(target), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xrename(oldname: *const c_char, newname: *const c_char) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::rename(oldname, newname);
|
||||
if r < 0 {
|
||||
perror!("rename {} -> {}", ptr_to_str(oldname), ptr_to_str(newname));
|
||||
}
|
||||
r
|
||||
libc::rename(oldname, newname)
|
||||
.as_os_result("rename", ptr_to_str(oldname), ptr_to_str(newname))
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xmkdir(path: *const c_char, mode: mode_t) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::mkdir(path, mode);
|
||||
if r < 0 && *errno() != libc::EEXIST {
|
||||
perror!("mkdir {}", ptr_to_str(path));
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(path) => path.mkdir(mode).log_cxx().map_or(-1, |_| 0),
|
||||
Err(_) => -1,
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,10 +350,7 @@ unsafe extern "C" fn xmkdir(path: *const c_char, mode: mode_t) -> i32 {
|
||||
unsafe extern "C" fn xmkdirs(path: *const c_char, mode: mode_t) -> i32 {
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => FsPath::from(p)
|
||||
.mkdirs(mode)
|
||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("mkdirs {} failed", p)))
|
||||
.map_or(-1, |_| 0),
|
||||
Ok(path) => path.mkdirs(mode).log_cxx().map_or(-1, |_| 0),
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
@@ -494,43 +364,39 @@ unsafe extern "C" fn xsendfile(
|
||||
count: usize,
|
||||
) -> isize {
|
||||
unsafe {
|
||||
let r = libc::sendfile(out_fd, in_fd, offset, count);
|
||||
if r < 0 {
|
||||
perror!("sendfile");
|
||||
}
|
||||
r
|
||||
libc::sendfile(out_fd, in_fd, offset, count)
|
||||
.as_os_result("sendfile", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xfork() -> i32 {
|
||||
unsafe {
|
||||
let r = libc::fork();
|
||||
if r < 0 {
|
||||
perror!("fork");
|
||||
}
|
||||
r
|
||||
libc::fork()
|
||||
.as_os_result("fork", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xpoll(fds: *mut pollfd, nfds: nfds_t, timeout: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::poll(fds, nfds, timeout);
|
||||
if r < 0 {
|
||||
perror!("poll");
|
||||
}
|
||||
r
|
||||
libc::poll(fds, nfds, timeout)
|
||||
.as_os_result("poll", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::mknod(pathname, mode, dev);
|
||||
if r < 0 {
|
||||
perror!("mknod {}", ptr_to_str(pathname));
|
||||
}
|
||||
r
|
||||
libc::mknod(pathname, mode, dev)
|
||||
.as_os_result("mknod", ptr_to_str(pathname), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pb-rs = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
base = { path = "../base" }
|
||||
cxx = { path = "../external/cxx-rs" }
|
||||
cxx = { workspace = true }
|
||||
byteorder = { workspace = true }
|
||||
size = { workspace = true }
|
||||
quick-protobuf = { workspace = true }
|
||||
@@ -25,8 +25,6 @@ p256 = { workspace = true }
|
||||
p384 = { workspace = true }
|
||||
p521 = { workspace = true }
|
||||
rsa = { workspace = true, features = ["sha2"] }
|
||||
block-buffer = { workspace = true }
|
||||
sec1 = { workspace = true }
|
||||
x509-cert = { workspace = true }
|
||||
der = { workspace = true, features = ["derive", "pem"] }
|
||||
fdt = { workspace = true }
|
||||
@@ -34,3 +32,7 @@ bytemuck = { workspace = true, features = ["derive", "min_const_generics"] }
|
||||
num-traits = { workspace = true }
|
||||
libz-rs-sys = { workspace = true }
|
||||
libbz2-rs-sys = { workspace = true }
|
||||
|
||||
# Pin version to prevent cargo update break builds
|
||||
block-buffer = { workspace = true }
|
||||
sec1 = { workspace = true }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use pb_rs::{types::FileDescriptor, ConfigBuilder};
|
||||
use pb_rs::{ConfigBuilder, types::FileDescriptor};
|
||||
|
||||
use crate::codegen::gen_cxx_binding;
|
||||
|
||||
|
||||
@@ -10,18 +10,18 @@ use std::process::exit;
|
||||
use std::str;
|
||||
|
||||
use argh::FromArgs;
|
||||
use bytemuck::{from_bytes, Pod, Zeroable};
|
||||
use bytemuck::{Pod, Zeroable, from_bytes};
|
||||
use num_traits::cast::AsPrimitive;
|
||||
use size::{Base, Size, Style};
|
||||
|
||||
use base::libc::{
|
||||
c_char, dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t, 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,
|
||||
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,
|
||||
};
|
||||
use base::{
|
||||
cstr_buf, log_err, map_args, BytesExt, EarlyExitExt, FsPath, LoggedResult, MappedFile,
|
||||
ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt,
|
||||
BytesExt, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt,
|
||||
cstr, log_err, map_args,
|
||||
};
|
||||
|
||||
use crate::check_env;
|
||||
@@ -342,13 +342,13 @@ impl Cpio {
|
||||
eprintln!("Extracting entry [{}] to [{}]", path, out);
|
||||
|
||||
let out = Utf8CStr::from_string(out);
|
||||
let out = FsPath::from(out);
|
||||
|
||||
let mut buf = cstr_buf::default();
|
||||
let mut buf = cstr::buf::default();
|
||||
|
||||
// Make sure its parent directories exist
|
||||
if out.parent(&mut buf) {
|
||||
FsPath::from(&buf).mkdirs(0o755)?;
|
||||
if let Some(dir) = out.parent_dir() {
|
||||
buf.push_str(dir);
|
||||
buf.mkdirs(0o755)?;
|
||||
}
|
||||
|
||||
let mode: mode_t = (entry.mode & 0o777).into();
|
||||
@@ -362,7 +362,7 @@ impl Cpio {
|
||||
S_IFLNK => {
|
||||
buf.clear();
|
||||
buf.push_str(str::from_utf8(entry.data.as_slice())?);
|
||||
FsPath::from(&buf).symlink_to(out)?;
|
||||
out.create_symlink_to(&buf)?;
|
||||
}
|
||||
S_IFBLK | S_IFCHR => {
|
||||
let dev = makedev(entry.rdevmajor.try_into()?, entry.rdevminor.try_into()?);
|
||||
@@ -399,7 +399,6 @@ impl Cpio {
|
||||
return Err(log_err!("path cannot end with / for add"));
|
||||
}
|
||||
let file = Utf8CStr::from_string(file);
|
||||
let file = FsPath::from(&file);
|
||||
let attr = file.get_attr()?;
|
||||
|
||||
let mut content = Vec::<u8>::new();
|
||||
@@ -413,8 +412,8 @@ impl Cpio {
|
||||
file.open(O_RDONLY | O_CLOEXEC)?.read_to_end(&mut content)?;
|
||||
mode | S_IFREG
|
||||
} else {
|
||||
rdevmajor = unsafe { major(attr.st.st_rdev.as_()) }.as_();
|
||||
rdevminor = unsafe { minor(attr.st.st_rdev.as_()) }.as_();
|
||||
rdevmajor = major(attr.st.st_rdev.as_()).as_();
|
||||
rdevminor = minor(attr.st.st_rdev.as_()).as_();
|
||||
if attr.is_block_device() {
|
||||
mode | S_IFBLK
|
||||
} else if attr.is_char_device() {
|
||||
@@ -765,7 +764,7 @@ pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool {
|
||||
CpioCli::from_args(&["magiskboot", "cpio"], &cmds).on_early_exit(print_cpio_usage);
|
||||
|
||||
let file = Utf8CStr::from_string(&mut cli.file);
|
||||
let mut cpio = if FsPath::from(file).exists() {
|
||||
let mut cpio = if file.exists() {
|
||||
Cpio::load_from_file(file)?
|
||||
} else {
|
||||
Cpio::new()
|
||||
|
||||
@@ -2,12 +2,12 @@ use std::{cell::UnsafeCell, process::exit};
|
||||
|
||||
use argh::FromArgs;
|
||||
use fdt::{
|
||||
node::{FdtNode, NodeProperty},
|
||||
Fdt, FdtError,
|
||||
node::{FdtNode, NodeProperty},
|
||||
};
|
||||
|
||||
use base::{
|
||||
libc::c_char, log_err, map_args, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr,
|
||||
EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, libc::c_char, log_err, map_args,
|
||||
};
|
||||
|
||||
use crate::{check_env, patch::patch_verity};
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(try_blocks)]
|
||||
|
||||
pub use libz_rs_sys::*;
|
||||
pub use libbz2_rs_sys::*;
|
||||
pub use base;
|
||||
use cpio::cpio_commands;
|
||||
use dtb::dtb_commands;
|
||||
pub use libbz2_rs_sys::*;
|
||||
pub use libz_rs_sys::*;
|
||||
use patch::hexpatch;
|
||||
use payload::extract_boot_from_payload;
|
||||
use sign::{get_sha, sha1_hash, sha256_hash, sign_boot_image, verify_boot_image, SHA};
|
||||
use sign::{SHA, get_sha, sha1_hash, sha256_hash, sign_boot_image, verify_boot_image};
|
||||
use std::env;
|
||||
|
||||
mod cpio;
|
||||
|
||||
@@ -9,10 +9,10 @@ use quick_protobuf::{BytesReader, MessageRead};
|
||||
|
||||
use crate::{
|
||||
ffi,
|
||||
proto::update_metadata::{mod_InstallOperation::Type, DeltaArchiveManifest},
|
||||
proto::update_metadata::{DeltaArchiveManifest, mod_InstallOperation::Type},
|
||||
};
|
||||
use base::{
|
||||
error, ffi::Utf8CStrRef, LoggedError, LoggedResult, ReadSeekExt, ResultExt, Utf8CStr, WriteExt,
|
||||
LoggedError, LoggedResult, ReadSeekExt, ResultExt, Utf8CStr, WriteExt, error, ffi::Utf8CStrRef,
|
||||
};
|
||||
|
||||
macro_rules! bad_payload {
|
||||
|
||||
@@ -15,18 +15,18 @@ use rsa::pkcs1v15::{
|
||||
Signature as RsaSignature, SigningKey as RsaSigningKey, VerifyingKey as RsaVerifyingKey,
|
||||
};
|
||||
use rsa::pkcs8::SubjectPublicKeyInfoRef;
|
||||
use rsa::signature::hazmat::{PrehashSigner, PrehashVerifier};
|
||||
use rsa::signature::SignatureEncoding;
|
||||
use rsa::signature::hazmat::{PrehashSigner, PrehashVerifier};
|
||||
use rsa::{RsaPrivateKey, RsaPublicKey};
|
||||
use sha1::Sha1;
|
||||
use sha2::{Sha256, Sha384, Sha512};
|
||||
use x509_cert::der::asn1::{OctetString, PrintableString};
|
||||
use x509_cert::der::Any;
|
||||
use x509_cert::spki::AlgorithmIdentifier;
|
||||
use x509_cert::Certificate;
|
||||
use x509_cert::der::Any;
|
||||
use x509_cert::der::asn1::{OctetString, PrintableString};
|
||||
use x509_cert::spki::AlgorithmIdentifier;
|
||||
|
||||
use base::libc::c_char;
|
||||
use base::{log_err, LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr};
|
||||
use base::{LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr, log_err};
|
||||
|
||||
use crate::ffi::BootImage;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <base.hpp>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <base.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use pb_rs::{types::FileDescriptor, ConfigBuilder};
|
||||
use pb_rs::{ConfigBuilder, types::FileDescriptor};
|
||||
|
||||
use crate::codegen::gen_cxx_binding;
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <flags.h>
|
||||
|
||||
using namespace std;
|
||||
@@ -129,13 +128,6 @@ static void poll_ctrl_handler(pollfd *pfd) {
|
||||
}
|
||||
}
|
||||
|
||||
void MagiskD::reboot() const noexcept {
|
||||
if (is_recovery())
|
||||
exec_command_sync("/system/bin/reboot", "recovery");
|
||||
else
|
||||
exec_command_sync("/system/bin/reboot");
|
||||
}
|
||||
|
||||
bool get_client_cred(int fd, sock_cred *cred) {
|
||||
socklen_t len = sizeof(ucred);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &len) != 0)
|
||||
@@ -326,19 +318,6 @@ static void handle_request(pollfd *pfd) {
|
||||
}
|
||||
}
|
||||
|
||||
static void switch_cgroup(const char *cgroup, int pid) {
|
||||
char buf[32];
|
||||
ssprintf(buf, sizeof(buf), "%s/cgroup.procs", cgroup);
|
||||
if (access(buf, F_OK) != 0)
|
||||
return;
|
||||
int fd = xopen(buf, O_WRONLY | O_APPEND | O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
return;
|
||||
ssprintf(buf, sizeof(buf), "%d\n", pid);
|
||||
xwrite(fd, buf, strlen(buf));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void daemon_entry() {
|
||||
android_logging();
|
||||
|
||||
@@ -360,51 +339,15 @@ static void daemon_entry() {
|
||||
if (fd > STDERR_FILENO)
|
||||
close(fd);
|
||||
|
||||
setsid();
|
||||
setcon(MAGISK_PROC_CON);
|
||||
|
||||
rust::daemon_entry();
|
||||
SDK_INT = MagiskD::Get().sdk_int();
|
||||
|
||||
// Escape from cgroup
|
||||
int pid = getpid();
|
||||
switch_cgroup("/acct", pid);
|
||||
switch_cgroup("/dev/cg2_bpf", pid);
|
||||
switch_cgroup("/sys/fs/cgroup", pid);
|
||||
if (get_prop("ro.config.per_app_memcg") != "false") {
|
||||
switch_cgroup("/dev/memcg/apps", pid);
|
||||
}
|
||||
|
||||
// Get self stat
|
||||
xstat("/proc/self/exe", &self_st);
|
||||
|
||||
// Samsung workaround #7887
|
||||
if (access("/system_ext/app/mediatek-res/mediatek-res.apk", F_OK) == 0) {
|
||||
set_prop("ro.vendor.mtk_model", "0");
|
||||
}
|
||||
|
||||
restore_tmpcon();
|
||||
|
||||
// Cleanups
|
||||
const char *tmp = get_magisk_tmp();
|
||||
char path[64];
|
||||
ssprintf(path, sizeof(path), "%s/" ROOTMNT, tmp);
|
||||
if (access(path, F_OK) == 0) {
|
||||
file_readline(true, path, [](string_view line) -> bool {
|
||||
umount2(line.data(), MNT_DETACH);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (getenv("REMOUNT_ROOT")) {
|
||||
xmount(nullptr, "/", nullptr, MS_REMOUNT | MS_RDONLY, nullptr);
|
||||
unsetenv("REMOUNT_ROOT");
|
||||
}
|
||||
ssprintf(path, sizeof(path), "%s/" ROOTOVL, tmp);
|
||||
rm_rf(path);
|
||||
|
||||
fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
sockaddr_un addr = {.sun_family = AF_LOCAL};
|
||||
ssprintf(addr.sun_path, sizeof(addr.sun_path), "%s/" MAIN_SOCKET, tmp);
|
||||
ssprintf(addr.sun_path, sizeof(addr.sun_path), "%s/" MAIN_SOCKET, get_magisk_tmp());
|
||||
unlink(addr.sun_path);
|
||||
if (xbind(fd, (sockaddr *) &addr, sizeof(addr)))
|
||||
exit(1);
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
use crate::consts::{MAGISK_FULL_VER, MAIN_CONFIG, SECURE_DIR};
|
||||
use crate::consts::{MAGISK_FULL_VER, MAGISK_PROC_CON, MAIN_CONFIG, ROOTMNT, ROOTOVL, SECURE_DIR};
|
||||
use crate::db::Sqlite3;
|
||||
use crate::ffi::{
|
||||
check_key_combo, disable_modules, exec_common_scripts, exec_module_scripts, get_magisk_tmp,
|
||||
initialize_denylist, setup_magisk_env, DbEntryKey, ModuleInfo, RequestCode,
|
||||
DbEntryKey, ModuleInfo, RequestCode, check_key_combo, disable_modules, exec_common_scripts,
|
||||
exec_module_scripts, get_magisk_tmp, initialize_denylist, setup_magisk_env,
|
||||
};
|
||||
use crate::get_prop;
|
||||
use crate::logging::{magisk_logging, setup_logfile, start_log_daemon};
|
||||
use crate::mount::{clean_mounts, setup_mounts};
|
||||
use crate::mount::{clean_mounts, setup_module_mount, setup_preinit_dir};
|
||||
use crate::package::ManagerInfo;
|
||||
use crate::selinux::restore_tmpcon;
|
||||
use crate::su::SuInfo;
|
||||
use base::libc::{O_CLOEXEC, O_RDONLY};
|
||||
use crate::{get_prop, set_prop};
|
||||
use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY};
|
||||
use base::{
|
||||
cstr, error, info, libc, open_fd, path, AtomicArc, BufReadExt, FsPathBuf, ResultExt, Utf8CStr,
|
||||
AtomicArc, BufReadExt, FsPathBuilder, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, error, info, libc,
|
||||
};
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::io::{BufReader, Write};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
@@ -78,10 +80,6 @@ impl MagiskD {
|
||||
unsafe { MAGISKD.get().unwrap_unchecked() }
|
||||
}
|
||||
|
||||
pub fn is_recovery(&self) -> bool {
|
||||
self.is_recovery
|
||||
}
|
||||
|
||||
pub fn zygisk_enabled(&self) -> bool {
|
||||
self.zygisk_enabled.load(Ordering::Acquire)
|
||||
}
|
||||
@@ -105,7 +103,7 @@ impl MagiskD {
|
||||
self.preserve_stub_apk();
|
||||
|
||||
// Check secure dir
|
||||
let secure_dir = path!(SECURE_DIR);
|
||||
let secure_dir = cstr!(SECURE_DIR);
|
||||
if !secure_dir.exists() {
|
||||
if self.sdk_int < 24 {
|
||||
secure_dir.mkdir(0o700).log_ok();
|
||||
@@ -146,7 +144,7 @@ impl MagiskD {
|
||||
Ordering::Release,
|
||||
);
|
||||
initialize_denylist();
|
||||
setup_mounts();
|
||||
setup_module_mount();
|
||||
let modules = self.handle_modules();
|
||||
self.module_list.set(modules).ok();
|
||||
clean_mounts();
|
||||
@@ -172,11 +170,12 @@ impl MagiskD {
|
||||
self.set_db_setting(DbEntryKey::BootloopCount, 0).log_ok();
|
||||
|
||||
// At this point it's safe to create the folder
|
||||
let secure_dir = path!(SECURE_DIR);
|
||||
let secure_dir = cstr!(SECURE_DIR);
|
||||
if !secure_dir.exists() {
|
||||
secure_dir.mkdir(0o700).log_ok();
|
||||
}
|
||||
|
||||
setup_preinit_dir();
|
||||
self.ensure_manager();
|
||||
self.zygisk_reset(true)
|
||||
}
|
||||
@@ -216,9 +215,26 @@ impl MagiskD {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reboot(&self) {
|
||||
if self.is_recovery {
|
||||
Command::new("/system/bin/reboot").arg("recovery").status()
|
||||
} else {
|
||||
Command::new("/system/bin/reboot").status()
|
||||
}
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn daemon_entry() {
|
||||
unsafe { libc::setsid() };
|
||||
|
||||
// Make sure the current context is magisk
|
||||
if let Ok(mut current) = cstr!("/proc/self/attr/current").open(O_WRONLY | O_CLOEXEC) {
|
||||
let con = cstr!(MAGISK_PROC_CON);
|
||||
current.write_all(con.as_bytes_with_nul()).log_ok();
|
||||
}
|
||||
|
||||
start_log_daemon();
|
||||
magisk_logging();
|
||||
info!("Magisk {} daemon started", MAGISK_FULL_VER);
|
||||
@@ -228,13 +244,13 @@ pub fn daemon_entry() {
|
||||
|| get_prop(cstr!("ro.product.device"), false).contains("vsoc");
|
||||
|
||||
// Load config status
|
||||
let path = FsPathBuf::<64>::new()
|
||||
.join(get_magisk_tmp())
|
||||
.join(MAIN_CONFIG);
|
||||
let magisk_tmp = get_magisk_tmp();
|
||||
let mut tmp_path = cstr::buf::new::<64>()
|
||||
.join_path(magisk_tmp)
|
||||
.join_path(MAIN_CONFIG);
|
||||
let mut is_recovery = false;
|
||||
if let Ok(file) = path.open(O_RDONLY | O_CLOEXEC) {
|
||||
let mut file = BufReader::new(file);
|
||||
file.foreach_props(|key, val| {
|
||||
if let Ok(main_config) = tmp_path.open(O_RDONLY | O_CLOEXEC) {
|
||||
BufReader::new(main_config).foreach_props(|key, val| {
|
||||
if key == "RECOVERYMODE" {
|
||||
is_recovery = val == "true";
|
||||
return false;
|
||||
@@ -242,11 +258,11 @@ pub fn daemon_entry() {
|
||||
true
|
||||
});
|
||||
}
|
||||
tmp_path.truncate(magisk_tmp.len());
|
||||
|
||||
let mut sdk_int = -1;
|
||||
if let Ok(file) = path!("/system/build.prop").open(O_RDONLY | O_CLOEXEC) {
|
||||
let mut file = BufReader::new(file);
|
||||
file.foreach_props(|key, val| {
|
||||
if let Ok(build_prop) = cstr!("/system/build.prop").open(O_RDONLY | O_CLOEXEC) {
|
||||
BufReader::new(build_prop).foreach_props(|key, val| {
|
||||
if key == "ro.build.version.sdk" {
|
||||
sdk_int = val.parse::<i32>().unwrap_or(-1);
|
||||
return false;
|
||||
@@ -262,6 +278,45 @@ pub fn daemon_entry() {
|
||||
}
|
||||
info!("* Device API level: {}", sdk_int);
|
||||
|
||||
restore_tmpcon().log_ok();
|
||||
|
||||
// Escape from cgroup
|
||||
let pid = unsafe { libc::getpid() };
|
||||
switch_cgroup("/acct", pid);
|
||||
switch_cgroup("/dev/cg2_bpf", pid);
|
||||
switch_cgroup("/sys/fs/cgroup", pid);
|
||||
if get_prop(cstr!("ro.config.per_app_memcg"), false) != "false" {
|
||||
switch_cgroup("/dev/memcg/apps", pid);
|
||||
}
|
||||
|
||||
// Samsung workaround #7887
|
||||
if cstr!("/system_ext/app/mediatek-res/mediatek-res.apk").exists() {
|
||||
set_prop(cstr!("ro.vendor.mtk_model"), cstr!("0"), false);
|
||||
}
|
||||
|
||||
// Cleanup pre-init mounts
|
||||
tmp_path.append_path(ROOTMNT);
|
||||
if let Ok(mount_list) = tmp_path.open(O_RDONLY | O_CLOEXEC) {
|
||||
BufReader::new(mount_list).foreach_lines(|line| {
|
||||
line.truncate(line.trim_end().len());
|
||||
let item = Utf8CStr::from_string(line);
|
||||
item.unmount().log_ok();
|
||||
true
|
||||
})
|
||||
}
|
||||
tmp_path.truncate(magisk_tmp.len());
|
||||
|
||||
// Remount rootfs as read-only if requested
|
||||
if std::env::var_os("REMOUNT_ROOT").is_some() {
|
||||
cstr!("/").remount_mount_flags(libc::MS_RDONLY).log_ok();
|
||||
unsafe { std::env::remove_var("REMOUNT_ROOT") };
|
||||
}
|
||||
|
||||
// Remove all pre-init overlay files to free-up memory
|
||||
tmp_path.append_path(ROOTOVL);
|
||||
tmp_path.remove_all().log_ok();
|
||||
tmp_path.truncate(magisk_tmp.len());
|
||||
|
||||
let magiskd = MagiskD {
|
||||
sdk_int,
|
||||
is_emulator,
|
||||
@@ -272,9 +327,22 @@ pub fn daemon_entry() {
|
||||
MAGISKD.set(magiskd).ok();
|
||||
}
|
||||
|
||||
fn switch_cgroup(cgroup: &str, pid: i32) {
|
||||
let mut buf = cstr::buf::new::<64>()
|
||||
.join_path(cgroup)
|
||||
.join_path("cgroup.procs");
|
||||
if !buf.exists() {
|
||||
return;
|
||||
}
|
||||
if let Ok(mut file) = buf.open(O_WRONLY | O_APPEND | O_CLOEXEC) {
|
||||
buf.clear();
|
||||
buf.write_fmt(format_args!("{}", pid)).ok();
|
||||
file.write_all(buf.as_bytes()).log_ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn check_data() -> bool {
|
||||
if let Ok(fd) = open_fd!(cstr!("/proc/mounts"), O_RDONLY | O_CLOEXEC) {
|
||||
let file = File::from(fd);
|
||||
if let Ok(file) = cstr!("/proc/mounts").open(O_RDONLY | O_CLOEXEC) {
|
||||
let mut mnt = false;
|
||||
BufReader::new(file).foreach_lines(|line| {
|
||||
if line.contains(" /data ") && !line.contains("tmpfs") {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#![allow(improper_ctypes, improper_ctypes_definitions)]
|
||||
use crate::daemon::{MagiskD, MAGISKD};
|
||||
use crate::daemon::{MAGISKD, MagiskD};
|
||||
use crate::ffi::{
|
||||
open_and_init_db, sqlite3, sqlite3_errstr, DbEntryKey, DbStatement, DbValues, MntNsMode,
|
||||
DbEntryKey, DbStatement, DbValues, MntNsMode, open_and_init_db, sqlite3, sqlite3_errstr,
|
||||
};
|
||||
use crate::socket::{IpcRead, IpcWrite};
|
||||
use DbArg::{Integer, Text};
|
||||
use base::{LoggedResult, ResultExt, Utf8CStr};
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
@@ -15,7 +16,6 @@ use std::pin::Pin;
|
||||
use std::ptr;
|
||||
use std::ptr::NonNull;
|
||||
use thiserror::Error;
|
||||
use DbArg::{Integer, Text};
|
||||
|
||||
fn sqlite_err_str(code: i32) -> &'static Utf8CStr {
|
||||
// SAFETY: sqlite3 always returns UTF-8 strings
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <base.hpp>
|
||||
#include <sqlite.hpp>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
#include "deny.hpp"
|
||||
|
||||
@@ -109,10 +108,10 @@ static bool proc_name_match(int pid, string_view name) {
|
||||
|
||||
bool proc_context_match(int pid, string_view context) {
|
||||
char buf[PATH_MAX];
|
||||
char con[1024];
|
||||
char con[1024] = {0};
|
||||
|
||||
sprintf(buf, "/proc/%d", pid);
|
||||
if (lgetfilecon(buf, { con, sizeof(con) }) >= 0) {
|
||||
if (lgetfilecon(buf, byte_data{ con, sizeof(con) })) {
|
||||
return str_starts(con, context);
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam};
|
||||
use syn::{Data, DeriveInput, Fields, GenericParam, parse_macro_input, parse_quote};
|
||||
|
||||
pub(crate) fn derive_decodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
int setcon(const char *con);
|
||||
int getfilecon(const char *path, byte_data con);
|
||||
int lgetfilecon(const char *path, byte_data con);
|
||||
int fgetfilecon(int fd, byte_data con);
|
||||
int setfilecon(const char *path, const char *con);
|
||||
int lsetfilecon(const char *path, const char *con);
|
||||
int fsetfilecon(int fd, const char *con);
|
||||
int getfilecon_at(int dirfd, const char *name, byte_data con);
|
||||
void setfilecon_at(int dirfd, const char *name, const char *con);
|
||||
|
||||
void restorecon();
|
||||
void restore_tmpcon();
|
||||
@@ -4,22 +4,25 @@
|
||||
#![feature(fn_traits)]
|
||||
#![feature(unix_socket_ancillary_data)]
|
||||
#![feature(unix_socket_peek)]
|
||||
#![feature(maybe_uninit_uninit_array)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
use crate::ffi::SuRequest;
|
||||
use crate::socket::Encodable;
|
||||
use base::{libc, Utf8CStr};
|
||||
use cxx::{type_id, ExternType};
|
||||
use daemon::{daemon_entry, MagiskD};
|
||||
use base::{Utf8CStr, libc};
|
||||
use cxx::{ExternType, type_id};
|
||||
use daemon::{MagiskD, daemon_entry};
|
||||
use derive::Decodable;
|
||||
use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging};
|
||||
use mount::{find_preinit_device, revert_unmount};
|
||||
use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
|
||||
use selinux::{lgetfilecon, lsetfilecon, restorecon, setfilecon};
|
||||
use socket::{recv_fd, recv_fds, send_fd, send_fds};
|
||||
use std::fs::File;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::DerefMut;
|
||||
use std::os::fd::FromRawFd;
|
||||
use su::{get_pty_num, pump_tty, restore_stdin};
|
||||
use zygisk::zygisk_should_load_module;
|
||||
|
||||
#[path = "../include/consts.rs"]
|
||||
@@ -30,6 +33,7 @@ mod logging;
|
||||
mod mount;
|
||||
mod package;
|
||||
mod resetprop;
|
||||
mod selinux;
|
||||
mod socket;
|
||||
mod su;
|
||||
mod zygisk;
|
||||
@@ -134,6 +138,8 @@ pub mod ffi {
|
||||
#[cxx_name = "prop_cb"]
|
||||
type PropCb;
|
||||
unsafe fn get_prop_rs(name: *const c_char, persist: bool) -> String;
|
||||
#[cxx_name = "set_prop"]
|
||||
unsafe fn set_prop_rs(name: *const c_char, value: *const c_char, skip_svc: bool) -> i32;
|
||||
unsafe fn prop_cb_exec(
|
||||
cb: Pin<&mut PropCb>,
|
||||
name: *const c_char,
|
||||
@@ -204,6 +210,13 @@ pub mod ffi {
|
||||
fn recv_fd(socket: i32) -> i32;
|
||||
fn recv_fds(socket: i32) -> Vec<i32>;
|
||||
unsafe fn write_to_fd(self: &SuRequest, fd: i32);
|
||||
fn pump_tty(infd: i32, outfd: i32);
|
||||
fn get_pty_num(fd: i32) -> i32;
|
||||
fn restore_stdin() -> bool;
|
||||
fn restorecon();
|
||||
fn lgetfilecon(path: Utf8CStrRef, con: &mut [u8]) -> bool;
|
||||
fn setfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool;
|
||||
fn lsetfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool;
|
||||
|
||||
#[namespace = "rust"]
|
||||
fn daemon_entry();
|
||||
@@ -219,7 +232,7 @@ pub mod ffi {
|
||||
// FFI for MagiskD
|
||||
extern "Rust" {
|
||||
type MagiskD;
|
||||
fn is_recovery(&self) -> bool;
|
||||
fn reboot(&self);
|
||||
fn sdk_int(&self) -> i32;
|
||||
fn zygisk_enabled(&self) -> bool;
|
||||
fn boot_stage_handler(&self, client: i32, code: i32);
|
||||
@@ -241,8 +254,6 @@ pub mod ffi {
|
||||
fn get() -> &'static MagiskD;
|
||||
}
|
||||
unsafe extern "C++" {
|
||||
#[allow(dead_code)]
|
||||
fn reboot(self: &MagiskD);
|
||||
fn handle_modules(self: &MagiskD) -> Vec<ModuleInfo>;
|
||||
}
|
||||
}
|
||||
@@ -267,3 +278,7 @@ impl SuRequest {
|
||||
pub fn get_prop(name: &Utf8CStr, persist: bool) -> String {
|
||||
unsafe { ffi::get_prop_rs(name.as_ptr(), persist) }
|
||||
}
|
||||
|
||||
pub fn set_prop(name: &Utf8CStr, value: &Utf8CStr, skip_svc: bool) -> bool {
|
||||
unsafe { ffi::set_prop_rs(name.as_ptr(), value.as_ptr(), skip_svc) == 0 }
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use crate::consts::{LOGFILE, LOG_PIPE};
|
||||
use crate::consts::{LOG_PIPE, LOGFILE};
|
||||
use crate::ffi::get_magisk_tmp;
|
||||
use crate::logging::LogFile::{Actual, Buffer};
|
||||
use base::libc::{
|
||||
getpid, gettid, localtime_r, pthread_sigmask, sigaddset, sigset_t, sigtimedwait, time_t,
|
||||
timespec, tm, O_CLOEXEC, O_RDWR, O_WRONLY, PIPE_BUF, SIGPIPE, SIG_BLOCK, SIG_SETMASK,
|
||||
O_CLOEXEC, O_RDWR, O_WRONLY, PIPE_BUF, SIG_BLOCK, SIG_SETMASK, SIGPIPE, getpid, gettid,
|
||||
localtime_r, pthread_sigmask, sigaddset, sigset_t, sigtimedwait, time_t, timespec, tm,
|
||||
};
|
||||
use base::{
|
||||
const_format::concatcp, cstr_buf, libc, raw_cstr, FsPathBuf, LogLevel, Logger, ReadExt,
|
||||
Utf8CStr, Utf8CStrBuf, WriteExt, LOGGER,
|
||||
FsPathBuilder, LOGGER, LogLevel, Logger, ReadExt, Utf8CStr, Utf8CStrBuf, WriteExt,
|
||||
const_format::concatcp, cstr, libc, raw_cstr,
|
||||
};
|
||||
use bytemuck::{bytes_of, write_zeroes, Pod, Zeroable};
|
||||
use bytemuck::{Pod, Zeroable, bytes_of, write_zeroes};
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
use std::cmp::min;
|
||||
@@ -130,7 +130,7 @@ fn write_log_to_pipe(mut logd: &File, prio: i32, msg: &Utf8CStr) -> io::Result<u
|
||||
let io2 = IoSlice::new(msg);
|
||||
let result = logd.write_vectored(&[io1, io2]);
|
||||
if let Err(ref e) = result {
|
||||
let mut buf = cstr_buf::default();
|
||||
let mut buf = cstr::buf::default();
|
||||
buf.write_fmt(format_args!("Cannot write_log_to_pipe: {}", e))
|
||||
.ok();
|
||||
android_log_write(LogLevel::Error, &buf);
|
||||
@@ -180,7 +180,9 @@ pub fn zygisk_get_logd() -> i32 {
|
||||
let mut fd = ZYGISK_LOGD.load(Ordering::Relaxed);
|
||||
if fd < 0 {
|
||||
android_logging();
|
||||
let path = FsPathBuf::default().join(get_magisk_tmp()).join(LOG_PIPE);
|
||||
let path = cstr::buf::default()
|
||||
.join_path(get_magisk_tmp())
|
||||
.join_path(LOG_PIPE);
|
||||
// Open as RW as sometimes it may block
|
||||
fd = unsafe { libc::open(path.as_ptr(), O_RDWR | O_CLOEXEC) };
|
||||
if fd >= 0 {
|
||||
@@ -266,7 +268,7 @@ extern "C" fn logfile_writer(arg: *mut c_void) -> *mut c_void {
|
||||
|
||||
let mut meta = LogMeta::zeroed();
|
||||
let mut msg_buf = [0u8; MAX_MSG_LEN];
|
||||
let mut aux = cstr_buf::new::<64>();
|
||||
let mut aux = cstr::buf::new::<64>();
|
||||
|
||||
loop {
|
||||
// Read request
|
||||
@@ -355,7 +357,9 @@ pub fn setup_logfile() {
|
||||
}
|
||||
|
||||
pub fn start_log_daemon() {
|
||||
let path = FsPathBuf::default().join(get_magisk_tmp()).join(LOG_PIPE);
|
||||
let path = cstr::buf::default()
|
||||
.join_path(get_magisk_tmp())
|
||||
.join_path(LOG_PIPE);
|
||||
|
||||
unsafe {
|
||||
libc::mkfifo(path.as_ptr(), 0o666);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <base.hpp>
|
||||
#include <consts.hpp>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <flags.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <base.hpp>
|
||||
#include <consts.hpp>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
use std::{
|
||||
cmp::Ordering::{Greater, Less},
|
||||
path::{Path, PathBuf},
|
||||
ptr,
|
||||
};
|
||||
|
||||
use num_traits::AsPrimitive;
|
||||
|
||||
use base::libc::{c_uint, dev_t};
|
||||
use base::{
|
||||
cstr, cstr_buf, debug, info, libc, parse_mount_info, raw_cstr, warn, FsPath, FsPathBuf,
|
||||
LibcReturn, LoggedResult, MountInfo, ResultExt, Utf8CStr,
|
||||
FsPathBuilder, LibcReturn, LoggedResult, MountInfo, ResultExt, Utf8CStr, Utf8CStrBuf, cstr,
|
||||
debug, info, libc, parse_mount_info, warn,
|
||||
};
|
||||
|
||||
use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR};
|
||||
use crate::ffi::{get_magisk_tmp, resolve_preinit_dir, switch_mnt_ns};
|
||||
use crate::get_prop;
|
||||
|
||||
pub fn setup_mounts() {
|
||||
info!("* Setup internal mounts");
|
||||
|
||||
pub fn setup_preinit_dir() {
|
||||
let magisk_tmp = get_magisk_tmp();
|
||||
|
||||
// Mount preinit directory
|
||||
let dev_path = FsPathBuf::<64>::new().join(magisk_tmp).join(PREINITDEV);
|
||||
let mut linked = false;
|
||||
let dev_path = cstr::buf::new::<64>()
|
||||
.join_path(magisk_tmp)
|
||||
.join_path(PREINITDEV);
|
||||
if let Ok(attr) = dev_path.get_attr() {
|
||||
if attr.st.st_mode & libc::S_IFMT as c_uint == libc::S_IFBLK.as_() {
|
||||
// DO NOT mount the block device directly, as we do not know the flags and configs
|
||||
@@ -32,7 +30,9 @@ pub fn setup_mounts() {
|
||||
// What we do instead is to scan through the current mountinfo and find a pre-existing
|
||||
// mount point mounting our desired partition, and then bind mount the target folder.
|
||||
let preinit_dev = attr.st.st_rdev;
|
||||
let mnt_path = FsPathBuf::default().join(magisk_tmp).join(PREINITMIRR);
|
||||
let mnt_path = cstr::buf::default()
|
||||
.join_path(magisk_tmp)
|
||||
.join_path(PREINITMIRR);
|
||||
for info in parse_mount_info("self") {
|
||||
if info.root == "/" && info.device == preinit_dev {
|
||||
if !info.fs_option.split(',').any(|s| s == "rw") {
|
||||
@@ -44,80 +44,48 @@ pub fn setup_mounts() {
|
||||
let mut preinit_dir = resolve_preinit_dir(target);
|
||||
let preinit_dir = Utf8CStr::from_string(&mut preinit_dir);
|
||||
let r: LoggedResult<()> = try {
|
||||
FsPath::from(preinit_dir).mkdir(0o700)?;
|
||||
let mut buf = cstr_buf::default();
|
||||
if mnt_path.parent(&mut buf) {
|
||||
FsPath::from(&buf).mkdirs(0o755)?;
|
||||
}
|
||||
preinit_dir.mkdir(0o700)?;
|
||||
mnt_path.mkdirs(0o755)?;
|
||||
mnt_path.remove().ok();
|
||||
unsafe {
|
||||
libc::symlink(preinit_dir.as_ptr(), mnt_path.as_ptr()).as_os_err()?
|
||||
}
|
||||
mnt_path.create_symlink_to(preinit_dir)?;
|
||||
};
|
||||
if r.is_ok() {
|
||||
linked = true;
|
||||
break;
|
||||
info!("* Found preinit dir: {}", preinit_dir);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !linked {
|
||||
warn!("mount: preinit dir not found");
|
||||
dev_path.remove().ok();
|
||||
} else {
|
||||
debug!("mount: preinit dir found");
|
||||
}
|
||||
|
||||
warn!("mount: preinit dir not found");
|
||||
}
|
||||
|
||||
pub fn setup_module_mount() {
|
||||
// Bind remount module root to clear nosuid
|
||||
let module_mnt = FsPathBuf::default().join(magisk_tmp).join(MODULEMNT);
|
||||
let module_mnt = cstr::buf::default()
|
||||
.join_path(get_magisk_tmp())
|
||||
.join_path(MODULEMNT);
|
||||
let _: LoggedResult<()> = try {
|
||||
module_mnt.mkdir(0o755)?;
|
||||
unsafe {
|
||||
libc::mount(
|
||||
raw_cstr!(MODULEROOT),
|
||||
module_mnt.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_BIND,
|
||||
ptr::null(),
|
||||
)
|
||||
.as_os_err()?;
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
module_mnt.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_REMOUNT | libc::MS_BIND | libc::MS_RDONLY,
|
||||
ptr::null(),
|
||||
)
|
||||
.as_os_err()?;
|
||||
}
|
||||
cstr!(MODULEROOT).bind_mount_to(&module_mnt)?;
|
||||
module_mnt.remount_mount_point_flags(libc::MS_RDONLY)?;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn clean_mounts() {
|
||||
let magisk_tmp = get_magisk_tmp();
|
||||
|
||||
let mut module_mnt = FsPathBuf::default().join(magisk_tmp).join(MODULEMNT);
|
||||
let _: LoggedResult<()> = try {
|
||||
unsafe {
|
||||
libc::umount2(module_mnt.as_ptr(), libc::MNT_DETACH).as_os_err()?;
|
||||
}
|
||||
};
|
||||
let mut buf = cstr::buf::default();
|
||||
|
||||
module_mnt.clear();
|
||||
let worker_dir = module_mnt.join(magisk_tmp).join(WORKERDIR);
|
||||
let module_mnt = buf.append_path(magisk_tmp).append_path(MODULEMNT);
|
||||
module_mnt.unmount().log_ok();
|
||||
buf.clear();
|
||||
|
||||
let worker_dir = buf.append_path(magisk_tmp).append_path(WORKERDIR);
|
||||
let _: LoggedResult<()> = try {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
worker_dir.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_PRIVATE | libc::MS_REC,
|
||||
ptr::null(),
|
||||
)
|
||||
.as_os_err()?;
|
||||
libc::umount2(worker_dir.as_ptr(), libc::MNT_DETACH).as_os_err()?;
|
||||
}
|
||||
worker_dir.set_mount_private(true)?;
|
||||
worker_dir.unmount()?;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -213,32 +181,26 @@ pub fn find_preinit_device() -> String {
|
||||
&& let Ok(tmp) = std::env::var("MAGISKTMP")
|
||||
&& !tmp.is_empty()
|
||||
{
|
||||
let mut mirror_dir = FsPathBuf::default().join(&tmp).join(PREINITMIRR);
|
||||
let preinit_dir = FsPath::from(Utf8CStr::from_string(&mut preinit_dir));
|
||||
let mut buf = cstr::buf::default();
|
||||
let mirror_dir = buf.append_path(&tmp).append_path(PREINITMIRR);
|
||||
let preinit_dir = Utf8CStr::from_string(&mut preinit_dir);
|
||||
let _: LoggedResult<()> = try {
|
||||
preinit_dir.mkdirs(0o700)?;
|
||||
let mut buf = cstr_buf::default();
|
||||
if mirror_dir.parent(&mut buf) {
|
||||
FsPath::from(&buf).mkdirs(0o755)?;
|
||||
}
|
||||
unsafe {
|
||||
libc::umount2(mirror_dir.as_ptr(), libc::MNT_DETACH)
|
||||
.as_os_err()
|
||||
.ok(); // ignore error
|
||||
mirror_dir.remove().ok();
|
||||
libc::symlink(preinit_dir.as_ptr(), mirror_dir.as_ptr()).as_os_err()?;
|
||||
}
|
||||
mirror_dir.mkdirs(0o755)?;
|
||||
mirror_dir.unmount().ok();
|
||||
mirror_dir.remove().ok();
|
||||
mirror_dir.create_symlink_to(preinit_dir)?;
|
||||
};
|
||||
if std::env::var_os("MAKEDEV").is_some() {
|
||||
mirror_dir.clear();
|
||||
let dev_path = mirror_dir.join(&tmp).join(PREINITDEV);
|
||||
buf.clear();
|
||||
let dev_path = buf.append_path(&tmp).append_path(PREINITDEV);
|
||||
unsafe {
|
||||
libc::mknod(
|
||||
dev_path.as_ptr(),
|
||||
libc::S_IFBLK | 0o600,
|
||||
info.device as dev_t,
|
||||
)
|
||||
.as_os_err()
|
||||
.check_io_err()
|
||||
.log()
|
||||
.ok();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::consts::{APP_PACKAGE_NAME, MAGISK_VER_CODE};
|
||||
use crate::daemon::{to_app_id, MagiskD, AID_APP_END, AID_APP_START, AID_USER_OFFSET};
|
||||
use crate::ffi::{get_magisk_tmp, install_apk, uninstall_pkg, DbEntryKey};
|
||||
use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY};
|
||||
use crate::daemon::{AID_APP_END, AID_APP_START, AID_USER_OFFSET, MagiskD, to_app_id};
|
||||
use crate::ffi::{DbEntryKey, get_magisk_tmp, install_apk, uninstall_pkg};
|
||||
use base::WalkResult::{Abort, Continue, Skip};
|
||||
use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY};
|
||||
use base::{
|
||||
cstr, cstr_buf, error, fd_get_attr, open_fd, warn, BufReadExt, Directory, FsPath, FsPathBuf,
|
||||
LoggedResult, ReadExt, ResultExt, Utf8CStrBuf,
|
||||
BufReadExt, Directory, FsPathBuilder, LoggedResult, ReadExt, ResultExt, Utf8CStrBuf,
|
||||
Utf8CString, cstr, error, fd_get_attr, warn,
|
||||
};
|
||||
use bit_set::BitSet;
|
||||
use cxx::CxxString;
|
||||
@@ -14,14 +14,13 @@ use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::pin::Pin;
|
||||
use std::time::Duration;
|
||||
|
||||
const EOCD_MAGIC: u32 = 0x06054B50;
|
||||
const APK_SIGNING_BLOCK_MAGIC: [u8; 16] = *b"APK Sig Block 42";
|
||||
const SIGNATURE_SCHEME_V2_MAGIC: u32 = 0x7109871A;
|
||||
const PACKAGES_XML: &str = "/data/system/packages.xml";
|
||||
|
||||
macro_rules! bad_apk {
|
||||
($msg:literal) => {
|
||||
@@ -152,15 +151,16 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
res.log().unwrap_or(vec![])
|
||||
}
|
||||
|
||||
fn find_apk_path(pkg: &str, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
fn find_apk_path(pkg: &str) -> LoggedResult<Utf8CString> {
|
||||
let mut buf = cstr::buf::default();
|
||||
Directory::open(cstr!("/data/app"))?.pre_order_walk(|e| {
|
||||
if !e.is_dir() {
|
||||
return Ok(Skip);
|
||||
}
|
||||
let name_bytes = e.name().to_bytes();
|
||||
let name_bytes = e.name().as_bytes();
|
||||
if name_bytes.starts_with(pkg.as_bytes()) && name_bytes[pkg.len()] == b'-' {
|
||||
// Found the APK path, we can abort now
|
||||
e.path(buf)?;
|
||||
e.resolve_path(&mut buf)?;
|
||||
return Ok(Abort);
|
||||
}
|
||||
if name_bytes.starts_with(b"~~") {
|
||||
@@ -171,7 +171,7 @@ fn find_apk_path(pkg: &str, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
if !buf.is_empty() {
|
||||
buf.push_str("/base.apk");
|
||||
}
|
||||
Ok(())
|
||||
Ok(buf.to_owned())
|
||||
}
|
||||
|
||||
enum Status {
|
||||
@@ -204,47 +204,41 @@ impl Default for ManagerInfo {
|
||||
|
||||
#[derive(Default)]
|
||||
struct TrackedFile {
|
||||
path: PathBuf,
|
||||
path: Utf8CString,
|
||||
timestamp: Duration,
|
||||
}
|
||||
|
||||
impl TrackedFile {
|
||||
fn new<T: AsRef<Path>>(path: T) -> TrackedFile {
|
||||
fn inner(path: &Path) -> TrackedFile {
|
||||
let meta = match path.metadata() {
|
||||
Ok(meta) => meta,
|
||||
Err(_) => return TrackedFile::default(),
|
||||
};
|
||||
let timestamp = Duration::new(meta.ctime() as u64, meta.ctime_nsec() as u32);
|
||||
TrackedFile {
|
||||
path: PathBuf::from(path),
|
||||
timestamp,
|
||||
}
|
||||
}
|
||||
inner(path.as_ref())
|
||||
fn new(path: Utf8CString) -> TrackedFile {
|
||||
let attr = match path.get_attr() {
|
||||
Ok(attr) => attr,
|
||||
Err(_) => return TrackedFile::default(),
|
||||
};
|
||||
let timestamp = Duration::new(attr.st.st_ctime as u64, attr.st.st_ctime_nsec as u32);
|
||||
TrackedFile { path, timestamp }
|
||||
}
|
||||
|
||||
fn is_same(&self) -> bool {
|
||||
if self.path.as_os_str().is_empty() {
|
||||
if self.path.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let meta = match self.path.metadata() {
|
||||
Ok(meta) => meta,
|
||||
let attr = match self.path.get_attr() {
|
||||
Ok(attr) => attr,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let timestamp = Duration::new(meta.ctime() as u64, meta.ctime_nsec() as u32);
|
||||
let timestamp = Duration::new(attr.st.st_ctime as u64, attr.st.st_ctime_nsec as u32);
|
||||
timestamp == self.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
impl ManagerInfo {
|
||||
fn check_dyn(&mut self, daemon: &MagiskD, user: i32, pkg: &str) -> Status {
|
||||
let apk = FsPathBuf::default()
|
||||
.join(daemon.app_data_dir())
|
||||
.join_fmt(user)
|
||||
.join(pkg)
|
||||
.join("dyn")
|
||||
.join("current.apk");
|
||||
let apk = cstr::buf::default()
|
||||
.join_path(daemon.app_data_dir())
|
||||
.join_path_fmt(user)
|
||||
.join_path(pkg)
|
||||
.join_path("dyn")
|
||||
.join_path("current.apk");
|
||||
let uid: i32;
|
||||
let cert = match apk.open(O_RDONLY | O_CLOEXEC) {
|
||||
Ok(mut fd) => {
|
||||
@@ -268,16 +262,15 @@ impl ManagerInfo {
|
||||
}
|
||||
|
||||
self.repackaged_app_id = to_app_id(uid);
|
||||
self.tracked_files.insert(user, TrackedFile::new(apk));
|
||||
self.tracked_files
|
||||
.insert(user, TrackedFile::new(apk.to_owned()));
|
||||
Status::Installed
|
||||
}
|
||||
|
||||
fn check_stub(&mut self, user: i32, pkg: &str) -> Status {
|
||||
let mut arr = cstr_buf::default();
|
||||
if find_apk_path(pkg, &mut arr).is_err() {
|
||||
let Ok(apk) = find_apk_path(pkg) else {
|
||||
return Status::NotInstalled;
|
||||
}
|
||||
let apk = FsPath::from(&arr);
|
||||
};
|
||||
|
||||
let cert = match apk.open(O_RDONLY | O_CLOEXEC) {
|
||||
Ok(mut fd) => read_certificate(&mut fd, -1),
|
||||
@@ -286,7 +279,7 @@ impl ManagerInfo {
|
||||
|
||||
if cert.is_empty() || (pkg == self.repackaged_pkg && cert != self.repackaged_cert) {
|
||||
error!("pkg: repackaged APK signature invalid: {}", apk);
|
||||
uninstall_pkg(apk);
|
||||
uninstall_pkg(&apk);
|
||||
return Status::CertMismatch;
|
||||
}
|
||||
|
||||
@@ -298,11 +291,9 @@ impl ManagerInfo {
|
||||
}
|
||||
|
||||
fn check_orig(&mut self, user: i32) -> Status {
|
||||
let mut arr = cstr_buf::default();
|
||||
if find_apk_path(APP_PACKAGE_NAME, &mut arr).is_err() {
|
||||
let Ok(apk) = find_apk_path(APP_PACKAGE_NAME) else {
|
||||
return Status::NotInstalled;
|
||||
}
|
||||
let apk = FsPath::from(&arr);
|
||||
};
|
||||
|
||||
let cert = match apk.open(O_RDONLY | O_CLOEXEC) {
|
||||
Ok(mut fd) => read_certificate(&mut fd, MAGISK_VER_CODE),
|
||||
@@ -328,12 +319,9 @@ impl ManagerInfo {
|
||||
let tmp_apk = cstr!("/data/stub.apk");
|
||||
let result: LoggedResult<()> = try {
|
||||
{
|
||||
let mut tmp_fd = File::from(open_fd!(
|
||||
tmp_apk,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
|
||||
0o600
|
||||
)?);
|
||||
io::copy(stub_fd, &mut tmp_fd)?;
|
||||
let mut tmp_apk_file =
|
||||
tmp_apk.create(O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0o600)?;
|
||||
io::copy(stub_fd, &mut tmp_apk_file)?;
|
||||
}
|
||||
// Seek the fd back to start
|
||||
stub_fd.seek(SeekFrom::Start(0))?;
|
||||
@@ -356,14 +344,14 @@ impl ManagerInfo {
|
||||
&& file.is_same()
|
||||
{
|
||||
// no APK
|
||||
if file.path == Path::new("/data/system/packages.xml") {
|
||||
if install {
|
||||
if &file.path == PACKAGES_XML {
|
||||
if install && !daemon.is_emulator {
|
||||
self.install_stub();
|
||||
}
|
||||
return (-1, "");
|
||||
}
|
||||
// dyn APK is still the same
|
||||
if file.path.starts_with(daemon.app_data_dir()) {
|
||||
if file.path.starts_with(daemon.app_data_dir().as_str()) {
|
||||
return (
|
||||
user * AID_USER_OFFSET + self.repackaged_app_id,
|
||||
&self.repackaged_pkg,
|
||||
@@ -402,7 +390,7 @@ impl ManagerInfo {
|
||||
)
|
||||
} else {
|
||||
(-1, "")
|
||||
}
|
||||
};
|
||||
}
|
||||
Status::NotInstalled => {
|
||||
daemon.rm_db_string(DbEntryKey::SuManager).ok();
|
||||
@@ -432,9 +420,9 @@ impl ManagerInfo {
|
||||
|
||||
// If we cannot find any manager, track packages.xml for new package installs
|
||||
self.tracked_files
|
||||
.insert(user, TrackedFile::new("/data/system/packages.xml"));
|
||||
.insert(user, TrackedFile::new(PACKAGES_XML.into()));
|
||||
|
||||
if install {
|
||||
if install && !daemon.is_emulator {
|
||||
self.install_stub();
|
||||
}
|
||||
(-1, "")
|
||||
@@ -443,10 +431,10 @@ impl ManagerInfo {
|
||||
|
||||
impl MagiskD {
|
||||
fn get_package_uid(&self, user: i32, pkg: &str) -> i32 {
|
||||
let path = FsPathBuf::default()
|
||||
.join(self.app_data_dir())
|
||||
.join_fmt(user)
|
||||
.join(pkg);
|
||||
let path = cstr::buf::default()
|
||||
.join_path(self.app_data_dir())
|
||||
.join_path_fmt(user)
|
||||
.join_path(pkg);
|
||||
path.get_attr()
|
||||
.map(|attr| attr.st.st_uid as i32)
|
||||
.unwrap_or(-1)
|
||||
@@ -455,7 +443,9 @@ impl MagiskD {
|
||||
pub fn preserve_stub_apk(&self) {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
|
||||
let apk = FsPathBuf::default().join(get_magisk_tmp()).join("stub.apk");
|
||||
let apk = cstr::buf::default()
|
||||
.join_path(get_magisk_tmp())
|
||||
.join_path("stub.apk");
|
||||
|
||||
if let Ok(mut fd) = apk.open(O_RDONLY | O_CLOEXEC) {
|
||||
info.trusted_cert = read_certificate(&mut fd, MAGISK_VER_CODE);
|
||||
@@ -518,7 +508,9 @@ impl MagiskD {
|
||||
match user_dir.read()? {
|
||||
None => break,
|
||||
Some(e) => {
|
||||
let attr = e.get_attr()?;
|
||||
let mut entry_path = cstr::buf::default();
|
||||
e.resolve_path(&mut entry_path)?;
|
||||
let attr = entry_path.get_attr()?;
|
||||
let app_id = to_app_id(attr.st.st_uid as i32);
|
||||
if (AID_APP_START..=AID_APP_END).contains(&app_id) {
|
||||
let app_no = app_id - AID_APP_START;
|
||||
|
||||
@@ -9,15 +9,15 @@ use std::{
|
||||
|
||||
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
|
||||
|
||||
use crate::ffi::{prop_cb_exec, PropCb};
|
||||
use crate::ffi::{PropCb, prop_cb_exec};
|
||||
use crate::resetprop::proto::persistent_properties::{
|
||||
mod_PersistentProperties::PersistentPropertyRecord, PersistentProperties,
|
||||
PersistentProperties, mod_PersistentProperties::PersistentPropertyRecord,
|
||||
};
|
||||
use base::const_format::concatcp;
|
||||
use base::libc::{O_CLOEXEC, O_RDONLY};
|
||||
use base::{
|
||||
clone_attr, cstr, debug, libc::mkstemp, path, Directory, FsPathBuf, LibcReturn, LoggedResult,
|
||||
MappedFile, SilentResultExt, Utf8CStr, WalkResult,
|
||||
Directory, FsPathBuilder, LibcReturn, LoggedResult, MappedFile, SilentResultExt, Utf8CStr,
|
||||
Utf8CStrBuf, WalkResult, clone_attr, cstr, debug, libc::mkstemp,
|
||||
};
|
||||
|
||||
const PERSIST_PROP_DIR: &str = "/data/property";
|
||||
@@ -64,11 +64,13 @@ impl PropExt for PersistentProperties {
|
||||
}
|
||||
|
||||
fn check_proto() -> bool {
|
||||
path!(PERSIST_PROP).exists()
|
||||
cstr!(PERSIST_PROP).exists()
|
||||
}
|
||||
|
||||
fn file_get_prop(name: &Utf8CStr) -> LoggedResult<String> {
|
||||
let path = FsPathBuf::default().join(PERSIST_PROP_DIR).join(name);
|
||||
let path = cstr::buf::default()
|
||||
.join_path(PERSIST_PROP_DIR)
|
||||
.join_path(name);
|
||||
let mut file = path.open(O_RDONLY | O_CLOEXEC).silent()?;
|
||||
debug!("resetprop: read prop from [{}]", path);
|
||||
let mut s = String::new();
|
||||
@@ -77,20 +79,23 @@ fn file_get_prop(name: &Utf8CStr) -> LoggedResult<String> {
|
||||
}
|
||||
|
||||
fn file_set_prop(name: &Utf8CStr, value: Option<&Utf8CStr>) -> LoggedResult<()> {
|
||||
let path = FsPathBuf::default().join(PERSIST_PROP_DIR).join(name);
|
||||
let path = cstr::buf::default()
|
||||
.join_path(PERSIST_PROP_DIR)
|
||||
.join_path(name);
|
||||
if let Some(value) = value {
|
||||
let mut tmp = FsPathBuf::default()
|
||||
.join(PERSIST_PROP_DIR)
|
||||
.join("prop.XXXXXX");
|
||||
let mut tmp = cstr::buf::default()
|
||||
.join_path(PERSIST_PROP_DIR)
|
||||
.join_path("prop.XXXXXX");
|
||||
{
|
||||
let mut f = unsafe {
|
||||
let fd = mkstemp(tmp.as_mut_ptr()).check_os_err()?;
|
||||
File::from_raw_fd(fd)
|
||||
mkstemp(tmp.as_mut_ptr())
|
||||
.as_os_result("mkstemp", None, None)
|
||||
.map(|fd| File::from_raw_fd(fd))?
|
||||
};
|
||||
f.write_all(value.as_bytes())?;
|
||||
}
|
||||
debug!("resetprop: write prop to [{}]", tmp);
|
||||
tmp.rename_to(path)?
|
||||
tmp.rename_to(&path)?
|
||||
} else {
|
||||
path.remove().silent()?;
|
||||
debug!("resetprop: unlink [{}]", path);
|
||||
@@ -110,16 +115,17 @@ fn proto_read_props() -> LoggedResult<PersistentProperties> {
|
||||
}
|
||||
|
||||
fn proto_write_props(props: &PersistentProperties) -> LoggedResult<()> {
|
||||
let mut tmp = FsPathBuf::default().join(concatcp!(PERSIST_PROP, ".XXXXXX"));
|
||||
let mut tmp = cstr::buf::default().join_path(concatcp!(PERSIST_PROP, ".XXXXXX"));
|
||||
{
|
||||
let f = unsafe {
|
||||
let fd = mkstemp(tmp.as_mut_ptr()).check_os_err()?;
|
||||
File::from_raw_fd(fd)
|
||||
mkstemp(tmp.as_mut_ptr())
|
||||
.as_os_result("mkstemp", None, None)
|
||||
.map(|fd| File::from_raw_fd(fd))?
|
||||
};
|
||||
debug!("resetprop: encode with protobuf [{}]", tmp);
|
||||
props.write_message(&mut Writer::new(BufWriter::new(f)))?;
|
||||
}
|
||||
clone_attr(path!(PERSIST_PROP), &tmp)?;
|
||||
clone_attr(cstr!(PERSIST_PROP), &tmp)?;
|
||||
tmp.rename_to(cstr!(PERSIST_PROP))?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -162,10 +168,8 @@ pub fn persist_get_props(mut prop_cb: Pin<&mut PropCb>) {
|
||||
let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR))?;
|
||||
dir.pre_order_walk(|e| {
|
||||
if e.is_file() {
|
||||
if let Ok(name) = Utf8CStr::from_cstr(e.name()) {
|
||||
if let Ok(mut value) = file_get_prop(name) {
|
||||
prop_cb.exec(name, Utf8CStr::from_string(&mut value));
|
||||
}
|
||||
if let Ok(mut value) = file_get_prop(e.name()) {
|
||||
prop_cb.exec(e.name(), Utf8CStr::from_string(&mut value));
|
||||
}
|
||||
}
|
||||
// Do not traverse recursively
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <core.hpp>
|
||||
#include <flags.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int setcon(const char *con) {
|
||||
int fd = open("/proc/self/attr/current", O_WRONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
size_t len = strlen(con) + 1;
|
||||
int rc = write(fd, con, len);
|
||||
close(fd);
|
||||
return rc != len;
|
||||
}
|
||||
|
||||
int getfilecon(const char *path, byte_data con) {
|
||||
return syscall(__NR_getxattr, path, XATTR_NAME_SELINUX, con.buf(), con.sz());
|
||||
}
|
||||
|
||||
int lgetfilecon(const char *path, byte_data con) {
|
||||
return syscall(__NR_lgetxattr, path, XATTR_NAME_SELINUX, con.buf(), con.sz());
|
||||
}
|
||||
|
||||
int fgetfilecon(int fd, byte_data con) {
|
||||
return syscall(__NR_fgetxattr, fd, XATTR_NAME_SELINUX, con.buf(), con.sz());
|
||||
}
|
||||
|
||||
int setfilecon(const char *path, const char *con) {
|
||||
return syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, con, strlen(con) + 1, 0);
|
||||
}
|
||||
|
||||
int lsetfilecon(const char *path, const char *con) {
|
||||
return syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, con, strlen(con) + 1, 0);
|
||||
}
|
||||
|
||||
int fsetfilecon(int fd, const char *con) {
|
||||
return syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, con, strlen(con) + 1, 0);
|
||||
}
|
||||
|
||||
int getfilecon_at(int dirfd, const char *name, byte_data con) {
|
||||
char path[4096];
|
||||
fd_pathat(dirfd, name, path, sizeof(path));
|
||||
return lgetfilecon(path, con);
|
||||
}
|
||||
|
||||
void setfilecon_at(int dirfd, const char *name, const char *con) {
|
||||
char path[4096];
|
||||
fd_pathat(dirfd, name, path, sizeof(path));
|
||||
lsetfilecon(path, con);
|
||||
}
|
||||
|
||||
#define UNLABEL_CON "u:object_r:unlabeled:s0"
|
||||
#define SYSTEM_CON "u:object_r:system_file:s0"
|
||||
#define ADB_CON "u:object_r:adb_data_file:s0"
|
||||
#define ROOT_CON "u:object_r:rootfs:s0"
|
||||
|
||||
static void restore_syscon_from_null(int dirfd) {
|
||||
struct dirent *entry;
|
||||
char con[1024];
|
||||
|
||||
if (fgetfilecon(dirfd, { con, sizeof(con) }) >= 0) {
|
||||
if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0)
|
||||
fsetfilecon(dirfd, SYSTEM_CON);
|
||||
}
|
||||
|
||||
auto dir = xopen_dir(dirfd);
|
||||
while ((entry = xreaddir(dir.get()))) {
|
||||
int fd = openat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
if (entry->d_type == DT_DIR) {
|
||||
restore_syscon_from_null(fd);
|
||||
continue;
|
||||
} else if (entry->d_type == DT_REG) {
|
||||
if (fgetfilecon(fd, { con, sizeof(con) }) >= 0) {
|
||||
if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0)
|
||||
fsetfilecon(fd, SYSTEM_CON);
|
||||
}
|
||||
} else if (entry->d_type == DT_LNK) {
|
||||
if (getfilecon_at(dirfd, entry->d_name, { con, sizeof(con) }) >= 0) {
|
||||
if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0)
|
||||
setfilecon_at(dirfd, entry->d_name, SYSTEM_CON);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void restore_syscon(int dirfd) {
|
||||
struct dirent *entry;
|
||||
|
||||
fsetfilecon(dirfd, SYSTEM_CON);
|
||||
fchown(dirfd, 0, 0);
|
||||
|
||||
auto dir = xopen_dir(dirfd);
|
||||
while ((entry = xreaddir(dir.get()))) {
|
||||
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
if (entry->d_type == DT_DIR) {
|
||||
restore_syscon(fd);
|
||||
continue;
|
||||
} else if (entry->d_type) {
|
||||
fsetfilecon(fd, SYSTEM_CON);
|
||||
fchown(fd, 0, 0);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
void restorecon() {
|
||||
int fd = xopen("/sys/fs/selinux/context", O_WRONLY | O_CLOEXEC);
|
||||
if (write(fd, ADB_CON, sizeof(ADB_CON)) >= 0)
|
||||
lsetfilecon(SECURE_DIR, ADB_CON);
|
||||
close(fd);
|
||||
lsetfilecon(MODULEROOT, SYSTEM_CON);
|
||||
restore_syscon_from_null(xopen(MODULEROOT, O_RDONLY | O_CLOEXEC));
|
||||
restore_syscon(xopen(DATABIN, O_RDONLY | O_CLOEXEC));
|
||||
}
|
||||
|
||||
void restore_tmpcon() {
|
||||
const char *tmp = get_magisk_tmp();
|
||||
if (tmp == "/sbin"sv)
|
||||
setfilecon(tmp, ROOT_CON);
|
||||
else
|
||||
chmod(tmp, 0711);
|
||||
|
||||
auto dir = xopen_dir(tmp);
|
||||
int dfd = dirfd(dir.get());
|
||||
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));)
|
||||
setfilecon_at(dfd, entry->d_name, SYSTEM_CON);
|
||||
|
||||
string logd = tmp + "/"s LOG_PIPE;
|
||||
setfilecon(logd.data(), MAGISK_LOG_CON);
|
||||
}
|
||||
108
native/src/core/selinux.rs
Normal file
108
native/src/core/selinux.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use crate::consts::{DATABIN, LOG_PIPE, MAGISK_LOG_CON, MODULEROOT, SECURE_DIR};
|
||||
use crate::ffi::get_magisk_tmp;
|
||||
use base::libc::{O_CLOEXEC, O_WRONLY};
|
||||
use base::{Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, libc};
|
||||
use std::io::Write;
|
||||
|
||||
const UNLABEL_CON: &Utf8CStr = cstr!("u:object_r:unlabeled:s0");
|
||||
const SYSTEM_CON: &Utf8CStr = cstr!("u:object_r:system_file:s0");
|
||||
const ADB_CON: &Utf8CStr = cstr!("u:object_r:adb_data_file:s0");
|
||||
const ROOT_CON: &Utf8CStr = cstr!("u:object_r:rootfs:s0");
|
||||
|
||||
fn restore_syscon_from_unlabeled(
|
||||
path: &mut dyn Utf8CStrBuf,
|
||||
con: &mut dyn Utf8CStrBuf,
|
||||
) -> LoggedResult<()> {
|
||||
let dir_path_len = path.len();
|
||||
if path.get_secontext(con).log().is_ok() && con.as_str() == UNLABEL_CON {
|
||||
path.set_secontext(SYSTEM_CON)?;
|
||||
}
|
||||
let mut dir = Directory::open(path)?;
|
||||
while let Some(ref e) = dir.read()? {
|
||||
path.truncate(dir_path_len);
|
||||
path.append_path(e.name());
|
||||
if e.is_dir() {
|
||||
restore_syscon_from_unlabeled(path, con)?;
|
||||
} else if (e.is_file() || e.is_symlink())
|
||||
&& path.get_secontext(con).log().is_ok()
|
||||
&& con.as_str() == UNLABEL_CON
|
||||
{
|
||||
path.set_secontext(SYSTEM_CON)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn restore_syscon(path: &mut dyn Utf8CStrBuf) -> LoggedResult<()> {
|
||||
let dir_path_len = path.len();
|
||||
path.set_secontext(SYSTEM_CON)?;
|
||||
unsafe { libc::lchown(path.as_ptr(), 0, 0) };
|
||||
let mut dir = Directory::open(path)?;
|
||||
while let Some(ref e) = dir.read()? {
|
||||
path.truncate(dir_path_len);
|
||||
path.append_path(e.name());
|
||||
if e.is_dir() {
|
||||
restore_syscon(path)?;
|
||||
} else if e.is_file() || e.is_symlink() {
|
||||
path.set_secontext(SYSTEM_CON)?;
|
||||
unsafe { libc::lchown(path.as_ptr(), 0, 0) };
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn restorecon() {
|
||||
if let Ok(mut file) = cstr!("/sys/fs/selinux/context")
|
||||
.open(O_WRONLY | O_CLOEXEC)
|
||||
.log()
|
||||
{
|
||||
if file.write_all(ADB_CON.as_bytes_with_nul()).is_ok() {
|
||||
cstr!(SECURE_DIR).set_secontext(ADB_CON).log_ok();
|
||||
}
|
||||
}
|
||||
|
||||
let mut path = cstr::buf::default();
|
||||
let mut con = cstr::buf::new::<1024>();
|
||||
path.push_str(MODULEROOT);
|
||||
path.set_secontext(SYSTEM_CON).log_ok();
|
||||
restore_syscon_from_unlabeled(&mut path, &mut con).log_ok();
|
||||
|
||||
path.clear();
|
||||
path.push_str(DATABIN);
|
||||
restore_syscon(&mut path).log_ok();
|
||||
}
|
||||
|
||||
pub(crate) fn restore_tmpcon() -> LoggedResult<()> {
|
||||
let tmp = get_magisk_tmp();
|
||||
if tmp == "/sbin" {
|
||||
tmp.set_secontext(ROOT_CON)?;
|
||||
} else {
|
||||
unsafe { libc::chmod(tmp.as_ptr(), 0o711) };
|
||||
}
|
||||
|
||||
let mut path = cstr::buf::default();
|
||||
let mut dir = Directory::open(tmp)?;
|
||||
while let Some(ref e) = dir.read()? {
|
||||
e.resolve_path(&mut path)?;
|
||||
path.set_secontext(SYSTEM_CON)?;
|
||||
}
|
||||
|
||||
path.clear();
|
||||
path.append_path(tmp).append_path(LOG_PIPE);
|
||||
path.set_secontext(cstr!(MAGISK_LOG_CON))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn lgetfilecon(path: &Utf8CStr, con: &mut [u8]) -> bool {
|
||||
let mut con = cstr::buf::wrap(con);
|
||||
path.get_secontext(&mut con).is_ok()
|
||||
}
|
||||
|
||||
pub(crate) fn setfilecon(path: &Utf8CStr, con: &Utf8CStr) -> bool {
|
||||
path.follow_link().set_secontext(con).is_ok()
|
||||
}
|
||||
|
||||
pub(crate) fn lsetfilecon(path: &Utf8CStr, con: &Utf8CStr) -> bool {
|
||||
path.set_secontext(con).is_ok()
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use base::{libc, warn, ReadExt, ResultExt, WriteExt};
|
||||
use bytemuck::{bytes_of, bytes_of_mut, Zeroable};
|
||||
use base::{ReadExt, ResultExt, WriteExt, libc, warn};
|
||||
use bytemuck::{Zeroable, bytes_of, bytes_of_mut};
|
||||
use std::io;
|
||||
use std::io::{ErrorKind, IoSlice, IoSliceMut, Read, Write};
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <consts.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::daemon::{to_app_id, to_user_id, MagiskD, AID_ROOT, AID_SHELL};
|
||||
use crate::UCred;
|
||||
use crate::daemon::{AID_ROOT, AID_SHELL, MagiskD, to_app_id, to_user_id};
|
||||
use crate::db::{DbSettings, MultiuserMode, RootAccess};
|
||||
use crate::ffi::{
|
||||
app_log, app_notify, app_request, exec_root_shell, SuAppRequest, SuPolicy, SuRequest,
|
||||
SuAppRequest, SuPolicy, SuRequest, app_log, app_notify, app_request, exec_root_shell,
|
||||
};
|
||||
use crate::socket::IpcRead;
|
||||
use crate::su::db::RootSettings;
|
||||
use crate::UCred;
|
||||
use base::{debug, error, exit_on_error, libc, warn, LoggedResult, ResultExt, WriteExt};
|
||||
use base::{LoggedResult, ResultExt, WriteExt, debug, error, exit_on_error, libc, warn};
|
||||
use std::fs::File;
|
||||
use std::os::fd::{FromRawFd, IntoRawFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::daemon::{
|
||||
to_app_id, to_user_id, MagiskD, AID_APP_END, AID_APP_START, AID_ROOT, AID_SHELL,
|
||||
AID_APP_END, AID_APP_START, AID_ROOT, AID_SHELL, MagiskD, to_app_id, to_user_id,
|
||||
};
|
||||
use crate::db::DbArg::Integer;
|
||||
use crate::db::{MultiuserMode, RootAccess, SqlTable, SqliteResult, SqliteReturn};
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
mod daemon;
|
||||
mod db;
|
||||
mod pts;
|
||||
|
||||
pub use daemon::SuInfo;
|
||||
pub use pts::{get_pty_num, pump_tty, restore_stdin};
|
||||
|
||||
@@ -1,294 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Tan Chee Eng (@tan-ce)
|
||||
*/
|
||||
|
||||
/*
|
||||
* pts.c
|
||||
*
|
||||
* Manages the pseudo-terminal driver on Linux/Android and provides some
|
||||
* helper functions to handle raw input mode and terminal window resizing
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
#include "pts.hpp"
|
||||
|
||||
/**
|
||||
* Helper functions
|
||||
*/
|
||||
// Ensures all the data is written out
|
||||
static int write_blocking(int fd, char *buf, ssize_t bufsz) {
|
||||
ssize_t ret, written;
|
||||
|
||||
written = 0;
|
||||
do {
|
||||
ret = write(fd, buf + written, bufsz - written);
|
||||
if (ret == -1) return -1;
|
||||
written += ret;
|
||||
} while (written < bufsz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pump data from input FD to output FD. If close_output is
|
||||
* true, then close the output FD when we're done.
|
||||
*/
|
||||
static void pump(int input, int output, bool close_output = true) {
|
||||
char buf[4096];
|
||||
int len;
|
||||
while ((len = read(input, buf, 4096)) > 0) {
|
||||
if (write_blocking(output, buf, len) == -1) break;
|
||||
}
|
||||
close(input);
|
||||
if (close_output) close(output);
|
||||
}
|
||||
|
||||
static void* pump_thread(void* data) {
|
||||
int *fds = (int*) data;
|
||||
pump(fds[0], fds[1]);
|
||||
delete[] fds;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void pump_async(int input, int output) {
|
||||
pthread_t writer;
|
||||
int *fds = new int[2];
|
||||
fds[0] = input;
|
||||
fds[1] = output;
|
||||
pthread_create(&writer, nullptr, pump_thread, fds);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pts_open
|
||||
*
|
||||
* Opens a pts device and returns the name of the slave tty device.
|
||||
*
|
||||
* Arguments
|
||||
* slave_name the name of the slave device
|
||||
* slave_name_size the size of the buffer passed via slave_name
|
||||
*
|
||||
* Return Values
|
||||
* on failure either -2 or -1 (errno set) is returned.
|
||||
* on success, the file descriptor of the master device is returned.
|
||||
*/
|
||||
int pts_open(char *slave_name, size_t slave_name_size) {
|
||||
int fdm;
|
||||
|
||||
// Open master ptmx device
|
||||
fdm = open("/dev/ptmx", O_RDWR);
|
||||
if (fdm == -1)
|
||||
goto error;
|
||||
|
||||
// Get the slave name
|
||||
if (ptsname_r(fdm, slave_name, slave_name_size - 1))
|
||||
goto error;
|
||||
|
||||
slave_name[slave_name_size - 1] = '\0';
|
||||
|
||||
// Grant, then unlock
|
||||
if (grantpt(fdm) == -1)
|
||||
goto error;
|
||||
|
||||
if (unlockpt(fdm) == -1)
|
||||
goto error;
|
||||
|
||||
return fdm;
|
||||
error:
|
||||
close(fdm);
|
||||
PLOGE("pts_open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_pty_num(int fd) {
|
||||
int pty_num = -1;
|
||||
if (ioctl(fd, TIOCGPTN, &pty_num) != 0) {
|
||||
LOGW("get_pty_num failed with %d: %s\n", errno, std::strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return pty_num;
|
||||
}
|
||||
|
||||
// Stores the previous termios of stdin
|
||||
static struct termios old_stdin;
|
||||
static int stdin_is_raw = 0;
|
||||
|
||||
/**
|
||||
* set_stdin_raw
|
||||
*
|
||||
* Changes stdin to raw unbuffered mode, disables echo,
|
||||
* auto carriage return, etc.
|
||||
*
|
||||
* Return Value
|
||||
* on failure -1, and errno is set
|
||||
* on success 0
|
||||
*/
|
||||
int set_stdin_raw() {
|
||||
struct termios termios{};
|
||||
|
||||
if (tcgetattr(STDIN_FILENO, &termios) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
old_stdin = termios;
|
||||
|
||||
cfmakeraw(&termios);
|
||||
|
||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios) < 0) {
|
||||
// https://blog.zhanghai.me/fixing-line-editing-on-android-8-0/
|
||||
if (tcsetattr(STDIN_FILENO, TCSADRAIN, &termios) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
stdin_is_raw = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* restore_stdin
|
||||
*
|
||||
* Restore termios on stdin to the state it was before
|
||||
* set_stdin_raw() was called. If set_stdin_raw() was
|
||||
* never called, does nothing and doesn't return an error.
|
||||
*
|
||||
* This function is async-safe.
|
||||
*
|
||||
* Return Value
|
||||
* on failure, -1 and errno is set
|
||||
* on success, 0
|
||||
*/
|
||||
int restore_stdin() {
|
||||
if (!stdin_is_raw) return 0;
|
||||
|
||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_stdin) < 0) {
|
||||
if (tcsetattr(STDIN_FILENO, TCSADRAIN, &old_stdin) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
stdin_is_raw = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Flag indicating whether the sigwinch watcher should terminate.
|
||||
static volatile bool close_sigwinch_watcher = false;
|
||||
|
||||
/**
|
||||
* Thread process. Wait for a SIGWINCH to be received, then update
|
||||
* the terminal size.
|
||||
*/
|
||||
static void *watch_sigwinch(void *data) {
|
||||
sigset_t winch;
|
||||
int *fds = (int *)data;
|
||||
int sig;
|
||||
|
||||
sigemptyset(&winch);
|
||||
sigaddset(&winch, SIGWINCH);
|
||||
pthread_sigmask(SIG_UNBLOCK, &winch, nullptr);
|
||||
|
||||
do {
|
||||
if (close_sigwinch_watcher)
|
||||
break;
|
||||
|
||||
// Get the new terminal size
|
||||
struct winsize w;
|
||||
if (ioctl(fds[0], TIOCGWINSZ, &w) == -1)
|
||||
continue;
|
||||
|
||||
// Set the new terminal size
|
||||
ioctl(fds[1], TIOCSWINSZ, &w);
|
||||
|
||||
} while (sigwait(&winch, &sig) == 0);
|
||||
delete[] fds;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* watch_sigwinch_async
|
||||
*
|
||||
* After calling this function, if the application receives
|
||||
* SIGWINCH, the terminal window size will be read from
|
||||
* "input" and set on "output".
|
||||
*
|
||||
* NOTE: This function blocks SIGWINCH and spawns a thread.
|
||||
* NOTE 2: This function must be called before any of the
|
||||
* pump functions.
|
||||
*
|
||||
* Arguments
|
||||
* master A file descriptor of the TTY window size to follow
|
||||
* slave A file descriptor of the TTY window size which is
|
||||
* to be set on SIGWINCH
|
||||
*
|
||||
* Return Value
|
||||
* on failure, -1 and errno will be set. In this case, no
|
||||
* thread has been spawned and SIGWINCH will not be
|
||||
* blocked.
|
||||
* on success, 0
|
||||
*/
|
||||
int watch_sigwinch_async(int master, int slave) {
|
||||
pthread_t watcher;
|
||||
int *fds = new int[2];
|
||||
|
||||
// Block SIGWINCH so sigwait can later receive it
|
||||
sigset_t winch;
|
||||
sigemptyset(&winch);
|
||||
sigaddset(&winch, SIGWINCH);
|
||||
if (pthread_sigmask(SIG_BLOCK, &winch, nullptr) == -1) {
|
||||
delete[] fds;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize some variables, then start the thread
|
||||
close_sigwinch_watcher = 0;
|
||||
fds[0] = master;
|
||||
fds[1] = slave;
|
||||
int ret = pthread_create(&watcher, nullptr, &watch_sigwinch, fds);
|
||||
if (ret != 0) {
|
||||
delete[] fds;
|
||||
errno = ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pump_stdin_async
|
||||
*
|
||||
* Forward data from STDIN to the given FD
|
||||
* in a separate thread
|
||||
*/
|
||||
void pump_stdin_async(int outfd) {
|
||||
// Put stdin into raw mode
|
||||
set_stdin_raw();
|
||||
|
||||
// Pump data from stdin to the PTY
|
||||
pump_async(STDIN_FILENO, outfd);
|
||||
}
|
||||
|
||||
/**
|
||||
* pump_stdout_blocking
|
||||
*
|
||||
* Forward data from the FD to STDOUT.
|
||||
* Returns when the remote end of the FD closes.
|
||||
*
|
||||
* Before returning, restores stdin settings.
|
||||
*/
|
||||
void pump_stdout_blocking(int infd) {
|
||||
// Pump data from stdout to PTY
|
||||
pump(infd, STDOUT_FILENO, false /* Don't close output when done */);
|
||||
|
||||
// Cleanup
|
||||
restore_stdin();
|
||||
close_sigwinch_watcher = true;
|
||||
raise(SIGWINCH);
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Tan Chee Eng (@tan-ce)
|
||||
*/
|
||||
|
||||
/*
|
||||
* pts.hpp
|
||||
*
|
||||
* Manages the pseudo-terminal driver on Linux/Android and provides some
|
||||
* helper functions to handle raw input mode and terminal window resizing
|
||||
*/
|
||||
|
||||
#ifndef _PTS_H_
|
||||
#define _PTS_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/**
|
||||
* pts_open
|
||||
*
|
||||
* Opens a pts device and returns the name of the slave tty device.
|
||||
*
|
||||
* Arguments
|
||||
* slave_name the name of the slave device
|
||||
* slave_name_size the size of the buffer passed via slave_name
|
||||
*
|
||||
* Return Values
|
||||
* on failure either -2 or -1 (errno set) is returned.
|
||||
* on success, the file descriptor of the master device is returned.
|
||||
*/
|
||||
int pts_open(char *slave_name, size_t slave_name_size);
|
||||
|
||||
|
||||
int get_pty_num(int fd);
|
||||
|
||||
/**
|
||||
* set_stdin_raw
|
||||
*
|
||||
* Changes stdin to raw unbuffered mode, disables echo,
|
||||
* auto carriage return, etc.
|
||||
*
|
||||
* Return Value
|
||||
* on failure -1, and errno is set
|
||||
* on success 0
|
||||
*/
|
||||
int set_stdin_raw(void);
|
||||
|
||||
/**
|
||||
* restore_stdin
|
||||
*
|
||||
* Restore termios on stdin to the state it was before
|
||||
* set_stdin_raw() was called. If set_stdin_raw() was
|
||||
* never called, does nothing and doesn't return an error.
|
||||
*
|
||||
* This function is async-safe.
|
||||
*
|
||||
* Return Value
|
||||
* on failure, -1 and errno is set
|
||||
* on success, 0
|
||||
*/
|
||||
int restore_stdin(void);
|
||||
|
||||
/**
|
||||
* watch_sigwinch_async
|
||||
*
|
||||
* After calling this function, if the application receives
|
||||
* SIGWINCH, the terminal window size will be read from
|
||||
* "input" and set on "output".
|
||||
*
|
||||
* NOTE: This function blocks SIGWINCH and spawns a thread.
|
||||
*
|
||||
* Arguments
|
||||
* master A file descriptor of the TTY window size to follow
|
||||
* slave A file descriptor of the TTY window size which is
|
||||
* to be set on SIGWINCH
|
||||
*
|
||||
* Return Value
|
||||
* on failure, -1 and errno will be set. In this case, no
|
||||
* thread has been spawned and SIGWINCH will not be
|
||||
* blocked.
|
||||
* on success, 0
|
||||
*/
|
||||
int watch_sigwinch_async(int master, int slave);
|
||||
|
||||
/**
|
||||
* pump_stdin_async
|
||||
*
|
||||
* Forward data from STDIN to the given FD
|
||||
* in a separate thread
|
||||
*/
|
||||
void pump_stdin_async(int outfd);
|
||||
|
||||
/**
|
||||
* pump_stdout_blocking
|
||||
*
|
||||
* Forward data from the FD to STDOUT.
|
||||
* Returns when the remote end of the FD closes.
|
||||
*
|
||||
* Before returning, restores stdin settings.
|
||||
*/
|
||||
void pump_stdout_blocking(int infd);
|
||||
|
||||
#endif
|
||||
170
native/src/core/su/pts.rs
Normal file
170
native/src/core/su/pts.rs
Normal file
@@ -0,0 +1,170 @@
|
||||
use base::{error, libc, warn};
|
||||
use libc::{
|
||||
POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, TCSAFLUSH,
|
||||
TIOCGWINSZ, TIOCSWINSZ, cfmakeraw, close, pipe, poll, pollfd, raise, read, sigaddset,
|
||||
sigemptyset, signalfd, signalfd_siginfo, sigprocmask, sigset_t, splice, tcgetattr, tcsetattr,
|
||||
termios, winsize,
|
||||
};
|
||||
use std::{ffi::c_int, mem::MaybeUninit, ptr::null_mut};
|
||||
|
||||
static mut OLD_STDIN: Option<termios> = None;
|
||||
const TIOCGPTN: u32 = 0x80045430;
|
||||
|
||||
unsafe extern "C" {
|
||||
// Don't use the declaration from the libc crate as request should be u32 not i32
|
||||
fn ioctl(fd: c_int, request: u32, ...) -> i32;
|
||||
}
|
||||
|
||||
pub fn get_pty_num(fd: i32) -> i32 {
|
||||
let mut pty_num = -1i32;
|
||||
if unsafe { ioctl(fd, TIOCGPTN, &mut pty_num) } != 0 {
|
||||
warn!("Failed to get pty number");
|
||||
}
|
||||
pty_num
|
||||
}
|
||||
|
||||
fn set_stdin_raw() -> bool {
|
||||
unsafe {
|
||||
let mut termios: termios = std::mem::zeroed();
|
||||
|
||||
if tcgetattr(STDIN_FILENO, &mut termios) < 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let old_c_oflag = termios.c_oflag;
|
||||
OLD_STDIN = Some(termios);
|
||||
|
||||
cfmakeraw(&mut termios);
|
||||
|
||||
// don't modify output flags, since we are not setting stdout raw
|
||||
termios.c_oflag = old_c_oflag;
|
||||
|
||||
if tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios) < 0
|
||||
&& tcsetattr(STDIN_FILENO, TCSADRAIN, &termios) < 0
|
||||
{
|
||||
warn!("Failed to set terminal attributes");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn restore_stdin() -> bool {
|
||||
unsafe {
|
||||
if let Some(ref termios) = OLD_STDIN {
|
||||
if tcsetattr(STDIN_FILENO, TCSAFLUSH, termios) < 0
|
||||
&& tcsetattr(STDIN_FILENO, TCSADRAIN, termios) < 0
|
||||
{
|
||||
warn!("Failed to restore terminal attributes");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
OLD_STDIN = None;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn resize_pty(outfd: i32) {
|
||||
let mut ws: winsize = unsafe { std::mem::zeroed() };
|
||||
if unsafe { ioctl(STDIN_FILENO, TIOCGWINSZ as u32, &mut ws) } >= 0 {
|
||||
unsafe { ioctl(outfd, TIOCSWINSZ as u32, &ws) };
|
||||
}
|
||||
}
|
||||
|
||||
fn pump_via_pipe(infd: i32, outfd: i32, pipe: &[c_int; 2]) -> bool {
|
||||
// usize::MAX will EINVAL in some kernels, use i32::MAX in case
|
||||
let s = unsafe { splice(infd, null_mut(), pipe[1], null_mut(), i32::MAX as _, 0) };
|
||||
if s < 0 {
|
||||
error!("splice error");
|
||||
return false;
|
||||
}
|
||||
if s == 0 {
|
||||
return true;
|
||||
}
|
||||
let s = unsafe { splice(pipe[0], null_mut(), outfd, null_mut(), s as usize, 0) };
|
||||
if s < 0 {
|
||||
error!("splice error");
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn pump_tty(infd: i32, outfd: i32) {
|
||||
set_stdin_raw();
|
||||
|
||||
let sfd = unsafe {
|
||||
let mut mask: sigset_t = std::mem::zeroed();
|
||||
sigemptyset(&mut mask);
|
||||
sigaddset(&mut mask, SIGWINCH);
|
||||
if sigprocmask(SIG_BLOCK, &mask, null_mut()) < 0 {
|
||||
error!("sigprocmask");
|
||||
}
|
||||
signalfd(-1, &mask, SFD_CLOEXEC)
|
||||
};
|
||||
|
||||
resize_pty(outfd);
|
||||
|
||||
let mut pfds = [
|
||||
pollfd {
|
||||
fd: if outfd > 0 { STDIN_FILENO } else { -1 },
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
},
|
||||
pollfd {
|
||||
fd: infd,
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
},
|
||||
pollfd {
|
||||
fd: sfd,
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
},
|
||||
];
|
||||
|
||||
let mut p: [c_int; 2] = [0; 2];
|
||||
if unsafe { pipe(&mut p as *mut c_int) } < 0 {
|
||||
error!("pipe error");
|
||||
return;
|
||||
}
|
||||
'poll: loop {
|
||||
let ready = unsafe { poll(pfds.as_mut_ptr(), pfds.len() as _, -1) };
|
||||
|
||||
if ready < 0 {
|
||||
error!("poll error");
|
||||
break;
|
||||
}
|
||||
|
||||
for pfd in &pfds {
|
||||
if pfd.revents & POLLIN != 0 {
|
||||
let res = if pfd.fd == STDIN_FILENO {
|
||||
pump_via_pipe(pfd.fd, outfd, &p)
|
||||
} else if pfd.fd == infd {
|
||||
pump_via_pipe(pfd.fd, STDOUT_FILENO, &p)
|
||||
} else if pfd.fd == sfd {
|
||||
resize_pty(outfd);
|
||||
let mut buf: [MaybeUninit<u8>; size_of::<signalfd_siginfo>()] =
|
||||
MaybeUninit::uninit_array();
|
||||
if unsafe { read(pfd.fd, buf.as_mut_ptr() as *mut _, buf.len()) } < 0 {
|
||||
error!("read error");
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !res {
|
||||
break 'poll;
|
||||
}
|
||||
} else if pfd.revents != 0 && pfd.fd == infd {
|
||||
unsafe { close(pfd.fd) };
|
||||
break 'poll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
restore_stdin();
|
||||
unsafe { raise(SIGWINCH) };
|
||||
}
|
||||
@@ -20,8 +20,6 @@
|
||||
#include <flags.h>
|
||||
#include <core.hpp>
|
||||
|
||||
#include "pts.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define DEFAULT_SHELL "/system/bin/sh"
|
||||
@@ -41,10 +39,11 @@ int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGI
|
||||
"Usage: su [options] [-] [user [argument...]]\n\n"
|
||||
"Options:\n"
|
||||
" -c, --command COMMAND Pass COMMAND to the invoked shell\n"
|
||||
" -i, --interactive Force pseudo-terminal allocation when using -c\n"
|
||||
" -g, --group GROUP Specify the primary group\n"
|
||||
" -G, --supp-group GROUP Specify a supplementary group.\n"
|
||||
" -G, --supp-group GROUP Specify a supplementary group\n"
|
||||
" The first specified supplementary group is also used\n"
|
||||
" as a primary group if the option -g is not specified.\n"
|
||||
" as a primary group if the option -g is not specified\n"
|
||||
" -Z, --context CONTEXT Change SELinux context\n"
|
||||
" -t, --target PID PID to take mount namespace from\n"
|
||||
" -h, --help Display this help message and exit\n"
|
||||
@@ -105,6 +104,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
{ "target", required_argument, nullptr, 't' },
|
||||
{ "group", required_argument, nullptr, 'g' },
|
||||
{ "supp-group", required_argument, nullptr, 'G' },
|
||||
{ "interactive", no_argument, nullptr, 'i' },
|
||||
{ nullptr, 0, nullptr, 0 },
|
||||
};
|
||||
|
||||
@@ -119,7 +119,9 @@ int su_client_main(int argc, char *argv[]) {
|
||||
strcpy(argv[i], "-M");
|
||||
}
|
||||
|
||||
while ((c = getopt_long(argc, argv, "c:hlmps:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
|
||||
bool interactive = false;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "c:hlimps:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
|
||||
switch (c) {
|
||||
case 'c': {
|
||||
string command;
|
||||
@@ -134,6 +136,9 @@ int su_client_main(int argc, char *argv[]) {
|
||||
}
|
||||
case 'h':
|
||||
usage(EXIT_SUCCESS);
|
||||
case 'i':
|
||||
interactive = true;
|
||||
break;
|
||||
case 'l':
|
||||
req.login = true;
|
||||
break;
|
||||
@@ -219,10 +224,11 @@ int su_client_main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// Determine which one of our streams are attached to a TTY
|
||||
interactive |= req.command.empty();
|
||||
int atty = 0;
|
||||
if (isatty(STDIN_FILENO)) atty |= ATTY_IN;
|
||||
if (isatty(STDOUT_FILENO)) atty |= ATTY_OUT;
|
||||
if (isatty(STDERR_FILENO)) atty |= ATTY_ERR;
|
||||
if (isatty(STDIN_FILENO) && interactive) atty |= ATTY_IN;
|
||||
if (isatty(STDOUT_FILENO) && interactive) atty |= ATTY_OUT;
|
||||
if (isatty(STDERR_FILENO) && interactive) atty |= ATTY_ERR;
|
||||
|
||||
// Send stdin
|
||||
send_fd(fd, (atty & ATTY_IN) ? -1 : STDIN_FILENO);
|
||||
@@ -241,9 +247,9 @@ int su_client_main(int argc, char *argv[]) {
|
||||
|
||||
if (atty) {
|
||||
setup_sighandlers(sighandler);
|
||||
watch_sigwinch_async(STDOUT_FILENO, ptmx);
|
||||
pump_stdin_async(ptmx);
|
||||
pump_stdout_blocking(ptmx);
|
||||
// if stdin is not a tty, if we pump to ptmx, our process may intercept the input to ptmx and
|
||||
// output to stdout, which cause the target process lost input.
|
||||
pump_tty(ptmx, (atty & ATTY_IN) ? ptmx : -1);
|
||||
}
|
||||
|
||||
// Get the exit code
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::consts::MODULEROOT;
|
||||
use crate::daemon::{to_user_id, MagiskD};
|
||||
use crate::daemon::{MagiskD, to_user_id};
|
||||
use crate::ffi::{
|
||||
get_magisk_tmp, restore_zygisk_prop, update_deny_flags, ZygiskRequest, ZygiskStateFlags,
|
||||
ZygiskRequest, ZygiskStateFlags, get_magisk_tmp, restore_zygisk_prop, update_deny_flags,
|
||||
};
|
||||
use crate::socket::{IpcRead, UnixSocketExt};
|
||||
use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, STDOUT_FILENO};
|
||||
use base::{
|
||||
cstr, cstr_buf, error, fork_dont_care, libc, open_fd, raw_cstr, warn, Directory, FsPathBuf,
|
||||
LoggedError, LoggedResult, ResultExt, WriteExt,
|
||||
Directory, FsPathBuilder, LoggedError, LoggedResult, ResultExt, WriteExt, cstr, error,
|
||||
fork_dont_care, libc, raw_cstr, warn,
|
||||
};
|
||||
use std::fmt::Write;
|
||||
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
||||
@@ -37,9 +37,11 @@ fn exec_zygiskd(is_64_bit: bool, remote: UnixStream) {
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let magisk = "magisk";
|
||||
|
||||
let exe = FsPathBuf::<64>::new().join(get_magisk_tmp()).join(magisk);
|
||||
let exe = cstr::buf::new::<64>()
|
||||
.join_path(get_magisk_tmp())
|
||||
.join_path(magisk);
|
||||
|
||||
let mut fd_str = cstr_buf::new::<16>();
|
||||
let mut fd_str = cstr::buf::new::<16>();
|
||||
write!(fd_str, "{}", remote.as_raw_fd()).ok();
|
||||
unsafe {
|
||||
libc::execl(
|
||||
@@ -183,13 +185,13 @@ impl MagiskD {
|
||||
let failed_ids: Vec<i32> = client.read_decodable()?;
|
||||
if let Some(module_list) = self.module_list.get() {
|
||||
for id in failed_ids {
|
||||
let path = FsPathBuf::default()
|
||||
.join(MODULEROOT)
|
||||
.join(&module_list[id as usize].name)
|
||||
.join("zygisk");
|
||||
let path = cstr::buf::default()
|
||||
.join_path(MODULEROOT)
|
||||
.join_path(&module_list[id as usize].name)
|
||||
.join_path("zygisk");
|
||||
// Create the unloaded marker file
|
||||
if let Ok(dir) = Directory::open(&path) {
|
||||
dir.open_fd(cstr!("unloaded"), O_CREAT | O_RDONLY, 0o644)
|
||||
dir.open_as_file_at(cstr!("unloaded"), O_CREAT | O_RDONLY, 0o644)
|
||||
.log()
|
||||
.ok();
|
||||
}
|
||||
@@ -202,8 +204,10 @@ impl MagiskD {
|
||||
fn get_mod_dir(&self, mut client: UnixStream) -> LoggedResult<()> {
|
||||
let id: i32 = client.read_decodable()?;
|
||||
let module = &self.module_list.get().unwrap()[id as usize];
|
||||
let dir = FsPathBuf::default().join(MODULEROOT).join(&module.name);
|
||||
let fd = open_fd!(&dir, O_RDONLY | O_CLOEXEC)?;
|
||||
let dir = cstr::buf::default()
|
||||
.join_path(MODULEROOT)
|
||||
.join_path(&module.name);
|
||||
let fd = dir.open(O_RDONLY | O_CLOEXEC)?;
|
||||
client.send_fds(&[fd.as_raw_fd()])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user