mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-12-13 00:00:28 -08:00
Compare commits
211 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c5f11b7dd | ||
|
|
7f7dda9ec2 | ||
|
|
9c1005ff0c | ||
|
|
5b36b4472c | ||
|
|
a3fcc64aaa | ||
|
|
f3078bc903 | ||
|
|
6072744f7e | ||
|
|
a87ad35a50 | ||
|
|
cf56d7e4ed | ||
|
|
e33a5eb307 | ||
|
|
e5b704eb32 | ||
|
|
56457bd325 | ||
|
|
bdbb3c6657 | ||
|
|
c4d7001489 | ||
|
|
c07bac9a63 | ||
|
|
d27d04783f | ||
|
|
58de5a7ec7 | ||
|
|
504a9b4746 | ||
|
|
cccb5a3e08 | ||
|
|
d75fa62cab | ||
|
|
3d43c3c5bc | ||
|
|
b570b363d9 | ||
|
|
b9968aa1e6 | ||
|
|
c0d77808f6 | ||
|
|
9679492c28 | ||
|
|
f3b68e6543 | ||
|
|
0dcfaaf5ff | ||
|
|
ba513dcb9a | ||
|
|
ebabc60477 | ||
|
|
cf565d0145 | ||
|
|
52a23e7904 | ||
|
|
9e22b80714 | ||
|
|
7eed9c4a6d | ||
|
|
bf42fce17e | ||
|
|
9d421226a7 | ||
|
|
7b9be8369e | ||
|
|
7cf4b819ae | ||
|
|
9e1aea33c3 | ||
|
|
8767a88854 | ||
|
|
47c0084641 | ||
|
|
54e6a790cf | ||
|
|
2a86bc8695 | ||
|
|
04538372c6 | ||
|
|
9430ed66cd | ||
|
|
96f8efc27a | ||
|
|
a90e8b6112 | ||
|
|
561c1fb798 | ||
|
|
806fec7017 | ||
|
|
b3da28eade | ||
|
|
166f6412c2 | ||
|
|
1e877808bc | ||
|
|
1777d9f751 | ||
|
|
309b99eac0 | ||
|
|
a5aa1b3917 | ||
|
|
aced0632ec | ||
|
|
4e801788d7 | ||
|
|
0b4baad78b | ||
|
|
201e32d4c4 | ||
|
|
0980cb6eb5 | ||
|
|
f75d23363b | ||
|
|
6c0ba66f17 | ||
|
|
f32ce7392e | ||
|
|
8bf382adad | ||
|
|
541ba357bb | ||
|
|
b6578b52e3 | ||
|
|
fb01c43ece | ||
|
|
b9a012c6e3 | ||
|
|
17684ed8a8 | ||
|
|
1b6b3b2cd5 | ||
|
|
acd8567586 | ||
|
|
e780c76c93 | ||
|
|
532c6caddf | ||
|
|
ef8d9be633 | ||
|
|
2cdbcc5666 | ||
|
|
c282a8f328 | ||
|
|
b9eab39541 | ||
|
|
20903784a4 | ||
|
|
3ec9ff7467 | ||
|
|
17d3a87b1f | ||
|
|
14c5c60863 | ||
|
|
70a80090c4 | ||
|
|
b6cb5d09cb | ||
|
|
69cfde4516 | ||
|
|
bdc83da098 | ||
|
|
f872a122a9 | ||
|
|
aa92e4cbd0 | ||
|
|
e603877a17 | ||
|
|
bb96477779 | ||
|
|
543ee79720 | ||
|
|
ea8cd98361 | ||
|
|
58849f28a8 | ||
|
|
d66c284bed | ||
|
|
693848280b | ||
|
|
396afaa181 | ||
|
|
05ed29133b | ||
|
|
a31c1e8084 | ||
|
|
21891230f2 | ||
|
|
47da76c5a5 | ||
|
|
6017ff2318 | ||
|
|
e16d604d0d | ||
|
|
d3d5703f3f | ||
|
|
62fe92d922 | ||
|
|
512e7be74f | ||
|
|
727abbea8f | ||
|
|
76f81ece62 | ||
|
|
495654f9ff | ||
|
|
95fec2100e | ||
|
|
623a879797 | ||
|
|
4c96d23f48 | ||
|
|
9bc8f6e9d7 | ||
|
|
e00e6509ee | ||
|
|
be5739508b | ||
|
|
38c867ea94 | ||
|
|
2a985ce6c0 | ||
|
|
e4f3fb36f3 | ||
|
|
b2f8792873 | ||
|
|
2065133e2d | ||
|
|
86da87f254 | ||
|
|
102a7f8723 | ||
|
|
e9afc15719 | ||
|
|
08527dde9b | ||
|
|
d9c3a3c9a9 | ||
|
|
fe89f9e55e | ||
|
|
73802aabac | ||
|
|
bc66733289 | ||
|
|
f4c93b2251 | ||
|
|
c079c598f2 | ||
|
|
8a2f0063d4 | ||
|
|
dfe4b33f2f | ||
|
|
2f7cfa7ab2 | ||
|
|
bdcb813ee6 | ||
|
|
f0751007f3 | ||
|
|
6ad993704c | ||
|
|
796c3009c7 | ||
|
|
144ff5e716 | ||
|
|
054a1e5ea4 | ||
|
|
a223f6056e | ||
|
|
a1fd7704e0 | ||
|
|
b94227efc9 | ||
|
|
3a7e782c07 | ||
|
|
8f6b33d790 | ||
|
|
f476daa041 | ||
|
|
acfde9458d | ||
|
|
82e969627a | ||
|
|
40766b3375 | ||
|
|
d274e45587 | ||
|
|
0a0eb3f710 | ||
|
|
81d054a525 | ||
|
|
2e185f4ec9 | ||
|
|
67f347f880 | ||
|
|
81542fc6a8 | ||
|
|
5aced279d6 | ||
|
|
3f016f785f | ||
|
|
a6427d081e | ||
|
|
8c7fbe20f9 | ||
|
|
469aba8ed0 | ||
|
|
6e8e4ad5da | ||
|
|
2f33d654e4 | ||
|
|
760b6385f1 | ||
|
|
91527500f9 | ||
|
|
e87d989ca3 | ||
|
|
64d61bae08 | ||
|
|
9862265465 | ||
|
|
624b7616d0 | ||
|
|
d53f33bed8 | ||
|
|
02e039d792 | ||
|
|
9f9333315e | ||
|
|
0d10b812fe | ||
|
|
b4fe4f3d10 | ||
|
|
ba93fcbda0 | ||
|
|
88d19a4ca4 | ||
|
|
af7b9ea898 | ||
|
|
09cd0468cf | ||
|
|
529aa754f5 | ||
|
|
3c7e865555 | ||
|
|
7877ac0c3b | ||
|
|
1442e29d0e | ||
|
|
9a7e9b736e | ||
|
|
c421e45fa0 | ||
|
|
8833d21ac3 | ||
|
|
1a3c522c94 | ||
|
|
c55aa92d4f | ||
|
|
212a303347 | ||
|
|
3f3568d8af | ||
|
|
1e3bcfc8cd | ||
|
|
a4ce9f6f05 | ||
|
|
65dc99744e | ||
|
|
c6d4740b0c | ||
|
|
9f91c8b59d | ||
|
|
2b3b087c29 | ||
|
|
e08d46aa76 | ||
|
|
feccc97a14 | ||
|
|
77eec3d21d | ||
|
|
ecaafd1b70 | ||
|
|
0d51997e46 | ||
|
|
463cbceb07 | ||
|
|
1437c5c63f | ||
|
|
52f1d50902 | ||
|
|
a839cb787e | ||
|
|
f621fb2060 | ||
|
|
2ccd8b8838 | ||
|
|
7ef0746c52 | ||
|
|
6f609f0dd7 | ||
|
|
ee2a30470a | ||
|
|
e11fb2c09e | ||
|
|
c6e9270590 | ||
|
|
3e2e171407 | ||
|
|
332f531a10 | ||
|
|
bae2c9bc63 | ||
|
|
5ac68f8df8 | ||
|
|
06d3b94804 |
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -10,8 +10,7 @@
|
||||
*.cmd text eol=crlf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
busybox binary
|
||||
futility binary
|
||||
chromeos/** binary
|
||||
*.jar binary
|
||||
*.exe binary
|
||||
*.apk binary
|
||||
|
||||
22
.gitignore
vendored
22
.gitignore
vendored
@@ -1,25 +1,7 @@
|
||||
obj/
|
||||
libs/
|
||||
*.zip
|
||||
*.jks
|
||||
|
||||
# Generated binaries
|
||||
zip_static/arm/*
|
||||
zip_static/arm64/*
|
||||
zip_static/x86/*
|
||||
zip_static/x64/*
|
||||
uninstaller/arm/*
|
||||
uninstaller/arm64/*
|
||||
uninstaller/x86/*
|
||||
uninstaller/x64/*
|
||||
# Copied binaries
|
||||
ziptools/zipadjust
|
||||
|
||||
# Generated scripts
|
||||
uninstaller/common/
|
||||
zip_static/common/magic_mask.sh
|
||||
zip_static/common/magisksu.sh
|
||||
zip_static/common/init.magisk.rc
|
||||
zip_static/common/custom_ramdisk_patch.sh
|
||||
zip_static/META-INF/com/google/android/update-binary
|
||||
|
||||
# Leave all busybox!
|
||||
!busybox
|
||||
|
||||
17
.gitmodules
vendored
17
.gitmodules
vendored
@@ -1,12 +1,15 @@
|
||||
[submodule "jni/sepolicy-inject"]
|
||||
path = jni/sepolicy-inject
|
||||
url = https://github.com/topjohnwu/sepolicy-inject
|
||||
[submodule "jni/resetprop"]
|
||||
path = jni/resetprop
|
||||
url = https://github.com/topjohnwu/resetprop.git
|
||||
[submodule "jni/selinux"]
|
||||
path = jni/selinux
|
||||
url = https://github.com/topjohnwu/selinux.git
|
||||
[submodule "jni/su"]
|
||||
path = jni/su
|
||||
url = https://github.com/topjohnwu/Superuser.git
|
||||
url = https://github.com/topjohnwu/MagiskSU.git
|
||||
[submodule "jni/ndk-compression"]
|
||||
path = jni/ndk-compression
|
||||
url = https://github.com/topjohnwu/ndk-compression.git
|
||||
[submodule "jni/magiskpolicy"]
|
||||
path = jni/magiskpolicy
|
||||
url = https://github.com/topjohnwu/magiskpolicy.git
|
||||
[submodule "MagiskManager"]
|
||||
path = MagiskManager
|
||||
url = https://github.com/topjohnwu/MagiskManager.git
|
||||
|
||||
674
LICENSE
Normal file
674
LICENSE
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
1
MagiskManager
Submodule
1
MagiskManager
Submodule
Submodule MagiskManager added at 3550d1e61c
85
README.MD
85
README.MD
@@ -1,11 +1,78 @@
|
||||
# Magisk
|
||||
###Static binaries included:
|
||||
* Busybox: http://forum.xda-developers.com/android/software-hacking/tool-busybox-flashable-archs-t3348543
|
||||
|
||||
###How to build Magisk
|
||||
1. Download and install NDK
|
||||
2. Add the NDK directory into PATH
|
||||
To check if the PATH is set correctly, try calling `which ndk-build` (`where ndk-build` on Windows) and see if it shows the NDK directory
|
||||
3. Unix-like users (e.g. Linux & MacOS) please execute `build.sh` through shell
|
||||
Windows users please execute `build.cmd` through cmd
|
||||
4. The scripts will show you further details
|
||||
## How to build Magisk
|
||||
|
||||
#### Building has been tested on 3 major platforms:
|
||||
|
||||
***macOS 10.12.5***
|
||||
***Ubuntu 17.04 x64***
|
||||
***Windows 10 Creators Update x64***
|
||||
|
||||
#### Environment Requirements
|
||||
|
||||
1. Python 3 **(>= 3.5)**: `python3` (or in some cases `python`) should be accessible
|
||||
2. Java runtime: `java` should be accessible
|
||||
3. (Unix only) C compiler: `gcc` should be accessible
|
||||
4. Android SDK: `ANDROID_HOME` environment variable should point to the Android SDK folder
|
||||
5. NDK: Install NDK using `sdkmanager`, or through Android SDK Manager
|
||||
6. Android build-tools: Should have build-tools version matching `MagiskManager/app/build.gradle` installed
|
||||
|
||||
#### Instructions and Notes
|
||||
|
||||
1. The python build script uses ANSI color codes to change the color of the terminal output. For Windows, this **only** works on Windows 10, as previous Windows console do not support them. If you insist to use an older Windows version, a quick Google search should provide many workarounds
|
||||
2. After installing the latest Python 3 on Windows (allow the installer to add Python to PATH, or you'll have to manually set the environment), instead of calling `python3` like most Unix environment, you should call `python` in shell (cmd or Powershell both OK). You can double check the version by `python --version`
|
||||
3. The build script will do several checks, it will refuse to run if the environment doesn't meet the requirements
|
||||
4. For further instructions, please check the built in help message by `python3 build.py -h`
|
||||
(Unix users can simply `./build.py -h`, Windows users, as mentioned, call `python` instead)
|
||||
5. Each action has its own help message, access them by commands like `python3 build.py all -h`
|
||||
6. To build Magisk for release (enabled through the `--release` flag, the script builds in debug mode by default), you will need to provide a Java keystore file, and place it in `release_signature.jks` to sign Magisk Manager APK for release builds. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually)
|
||||
7. To properly setup the Android SDK environment, the easiest way is to use Android Studio and open Magisk Manager. If gradle sync passed, your build-tools etc. should be set properly. You can also access SDK Manager GUI within Android Studio to download NDK. Don't forget to add Android Studio's SDK path into environment variable ANDROID_HOME.
|
||||
|
||||
## License
|
||||
|
||||
Magisk, including all subprojects (git submodule) is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
## Credits
|
||||
|
||||
**MagiskManager** (`MagiskManager`)
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* All contributors and translators
|
||||
|
||||
**MagiskSU** (`jni/su`)
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
|
||||
* Copyright 2013, Koushik Dutta (@koush)
|
||||
* Copyright 2010, Adam Shanks (@ChainsDD)
|
||||
* Copyright 2008, Zinx Verituse (@zinxv)
|
||||
|
||||
**MagiskPolicy** (`jni/magiskpolicy`)
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
|
||||
* Copyright 2015, Joshua Brindle (@joshua_brindle)
|
||||
|
||||
**MagiskHide** (`jni/magiskhide`)
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* Copyright 2016, Pierre-Hugues Husson (phh@phh.me) (original hidesu)
|
||||
|
||||
**resetprop** (`jni/resetprop`)
|
||||
* Copyright 2016-2017 John Wu (@topjohnwu)
|
||||
* Copyright 2016 nkk71 (nkk71x@gmail.com)
|
||||
|
||||
**SELinux** (`jni/selinux`)
|
||||
* Makefile for NDK: Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
* It is maintained by many developers in SELinux project, copyright belongs to them
|
||||
|
||||
**ndk-compression** (`jni/ndk-compression`)
|
||||
* Makefile for NDK: Copyright 2017, John Wu (@topjohnwu)
|
||||
* Each library has its own copyright message in each directories
|
||||
|
||||
**Others Not Mentioned**
|
||||
* Copyright 2016-2017, John Wu (@topjohnwu)
|
||||
|
||||
167
build.cmd
167
build.cmd
@@ -1,167 +0,0 @@
|
||||
@ECHO OFF
|
||||
SETLOCAL ENABLEEXTENSIONS
|
||||
SET me=%~nx0
|
||||
SET parent=%~dp0
|
||||
SET tab=
|
||||
SET OK=
|
||||
|
||||
CD %parent%
|
||||
|
||||
call :%~1 "%~2"
|
||||
IF NOT DEFINED OK CALL :usage
|
||||
|
||||
EXIT /B %ERRORLEVEL%
|
||||
|
||||
:usage
|
||||
ECHO %me% all ^<version name^>
|
||||
ECHO %tab%Build binaries, zip, and sign Magisk
|
||||
ECHO %tab%This is equlivant to first ^<build^>, then ^<zip^>
|
||||
ECHO %me% clean
|
||||
ECHO %tab%Cleanup compiled / generated files
|
||||
ECHO %me% build
|
||||
ECHO %tab%Build the binaries with ndk
|
||||
ECHO %me% zip ^<version name^>
|
||||
ECHO %tab%Zip and sign Magisk
|
||||
ECHO %me% uninstaller
|
||||
ECHO %tab%Zip and sign the uninstaller
|
||||
EXIT /B 1
|
||||
|
||||
:all
|
||||
SET OK=y
|
||||
IF [%~1] == [] (
|
||||
CALL :error "Missing version number"
|
||||
CALL :usage
|
||||
EXIT /B %ERRORLEVEL%
|
||||
)
|
||||
CALL :build
|
||||
CALL :zip "%~1"
|
||||
EXIT /B %ERRORLEVEL%
|
||||
|
||||
:build
|
||||
SET OK=y
|
||||
ECHO ************************
|
||||
ECHO * Building binaries
|
||||
ECHO ************************
|
||||
FOR /F "tokens=* USEBACKQ" %%F IN (`where ndk-build`) DO (
|
||||
IF [%%F] == [] (
|
||||
CALL :error "Please add ndk-build to PATH!"
|
||||
EXIT /B 1
|
||||
)
|
||||
)
|
||||
CALL ndk-build -j4 || CALL :error "Magisk binary tools build failed...."
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
ECHO ************************
|
||||
ECHO * Copying binaries
|
||||
ECHO ************************
|
||||
COPY /Y libs\armeabi-v7a\* zip_static\arm
|
||||
COPY /Y libs\arm64-v8a\* zip_static\arm64
|
||||
COPY /Y libs\x86\* zip_static\x86
|
||||
COPY /Y libs\x86_64\* zip_static\x64
|
||||
CALL :mkcp libs\armeabi-v7a\bootimgtools uninstaller\arm
|
||||
CALL :mkcp libs\arm64-v8a\bootimgtools uninstaller\arm64
|
||||
CALL :mkcp libs\x86\bootimgtools uninstaller\x86
|
||||
CALL :mkcp libs\x86_64\bootimgtools uninstaller\x64
|
||||
EXIT /B %ERRORLEVEL%
|
||||
|
||||
:clean
|
||||
SET OK=y
|
||||
ECHO ************************
|
||||
ECHO * Cleaning up
|
||||
ECHO ************************
|
||||
CALL ndk-build clean
|
||||
forfiles /P zip_static\arm /C "cmd /C IF NOT @file == \"busybox\" DEL @file"
|
||||
forfiles /P zip_static\arm64 /C "cmd /C IF NOT @file == \"busybox\" DEL @file"
|
||||
forfiles /P zip_static\x86 /C "cmd /C IF NOT @file == \"busybox\" DEL @file"
|
||||
forfiles /P zip_static\x64 /C "cmd /C IF NOT @file == \"busybox\" DEL @file"
|
||||
2>NUL DEL zip_static\META-INF\com\google\android\update-binary
|
||||
2>NUL DEL zip_static\common\custom_ramdisk_patch.sh
|
||||
2>NUL DEL zip_static\common\magisksu.sh
|
||||
2>NUL DEL zip_static\common\init.magisk.rc
|
||||
2>NUL DEL zip_static\common\magic_mask.sh
|
||||
2>NUL RMDIR /S /Q uninstaller\common
|
||||
2>NUL RMDIR /S /Q uninstaller\arm
|
||||
2>NUL RMDIR /S /Q uninstaller\arm64
|
||||
2>NUL RMDIR /S /Q uninstaller\x86
|
||||
2>NUL RMDIR /S /Q uninstaller\x64
|
||||
EXIT /B 0
|
||||
|
||||
:zip
|
||||
SET OK=y
|
||||
IF [%~1] == [] (
|
||||
CALL :error "Missing version number"
|
||||
CALL :usage
|
||||
EXIT /B %ERRORLEVEL%
|
||||
)
|
||||
IF NOT EXIST "zip_static\arm\bootimgtools" CALL :error "Missing binaries! Please run '%me% build' before zipping!"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
ECHO ************************
|
||||
ECHO * Adding version info
|
||||
ECHO ************************
|
||||
powershell.exe -nologo -noprofile -command "(gc -Raw scripts\flash_script.sh) -replace 'MAGISK_VERSION_STUB', 'Magisk v%~1 Boot Image Patcher' | sc zip_static\META-INF\com\google\android\update-binary"
|
||||
powershell.exe -nologo -noprofile -command "(gc -Raw scripts\magic_mask.sh) -replace 'MAGISK_VERSION_STUB', 'setprop magisk.version \"%~1\"' | sc zip_static\common\magic_mask.sh"
|
||||
ECHO ************************
|
||||
ECHO * Zipping Magisk v%~1
|
||||
ECHO ************************
|
||||
COPY /Y scripts\custom_ramdisk_patch.sh zip_static\common\custom_ramdisk_patch.sh
|
||||
COPY /Y scripts\magisksu.sh zip_static\common\magisksu.sh
|
||||
COPY /Y scripts\init.magisk.rc zip_static\common\init.magisk.rc
|
||||
CD zip_static
|
||||
2>NUL DEL "..\Magisk-v%~1.zip"
|
||||
..\ziptools\win_bin\zip "..\Magisk-v%~1.zip" -r .
|
||||
CD ..\
|
||||
CALL :sign_zip "Magisk-v%~1.zip"
|
||||
EXIT /B %ERRORLEVEL%
|
||||
|
||||
:uninstaller
|
||||
SET OK=y
|
||||
IF NOT EXIST "uninstaller\arm\bootimgtools" CALL :error "Missing binaries! Please run '%me% build' before zipping!"
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
ECHO ************************
|
||||
ECHO * Zipping uninstaller
|
||||
ECHO ************************
|
||||
CALL :mkcp scripts\magisk_uninstaller.sh uninstaller\common
|
||||
FOR /F "tokens=* USEBACKQ" %%F IN (`ziptools\win_bin\date "+%%Y%%m%%d"`) DO (set timestamp=%%F)
|
||||
CD uninstaller
|
||||
2>NUL DEL "../Magisk-uninstaller-%timestamp%.zip"
|
||||
..\ziptools\win_bin\zip "../Magisk-uninstaller-%timestamp%.zip" -r .
|
||||
CD ..\
|
||||
CALL :sign_zip "Magisk-uninstaller-%timestamp%.zip"
|
||||
EXIT /B %ERRORLEVEL%
|
||||
|
||||
:sign_zip
|
||||
IF NOT EXIST "ziptools\win_bin\zipadjust.exe" (
|
||||
ECHO ************************
|
||||
ECHO * Compiling ZipAdjust
|
||||
ECHO ************************
|
||||
gcc -o ziptools\win_bin\zipadjust ziptools\src\*.c -lz || CALL :error "ZipAdjust Build failed...."
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
)
|
||||
SET basename="%~1"
|
||||
SET basename="%basename:.zip=%"
|
||||
ECHO ************************
|
||||
ECHO * First sign %~1
|
||||
ECHO ************************
|
||||
java -jar "ziptools\signapk.jar" "ziptools\test.certificate.x509.pem" "ziptools\test.key.pk8" "%~1" "%basename:"=%-firstsign.zip"
|
||||
ECHO ************************
|
||||
ECHO * Adjusting %~1
|
||||
ECHO ************************
|
||||
ziptools\win_bin\zipadjust "%basename:"=%-firstsign.zip" "%basename:"=%-adjusted.zip"
|
||||
ECHO ************************
|
||||
ECHO * Final sign %~1
|
||||
ECHO ************************
|
||||
java -jar "ziptools\minsignapk.jar" "ziptools\test.certificate.x509.pem" "ziptools\test.key.pk8" "%basename:"=%-adjusted.zip" "%basename:"=%-signed.zip"
|
||||
|
||||
MOVE /Y "%basename:"=%-signed.zip" "%~1"
|
||||
DEL "%basename:"=%-adjusted.zip" "%basename:"=%-firstsign.zip"
|
||||
EXIT /B %ERRORLEVEL%
|
||||
|
||||
:mkcp
|
||||
2>NUL MKDIR "%~2"
|
||||
2>NUL COPY /Y "%~1" "%~2"
|
||||
EXIT /B 0
|
||||
|
||||
:error
|
||||
ECHO.
|
||||
ECHO ! %~1
|
||||
ECHO.
|
||||
EXIT /B 1
|
||||
310
build.py
Executable file
310
build.py
Executable file
@@ -0,0 +1,310 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
def error(str):
|
||||
print('\n' + '\033[41m' + str + '\033[0m' + '\n')
|
||||
sys.exit(1)
|
||||
|
||||
def header(str):
|
||||
print('\n' + '\033[44m' + str + '\033[0m' + '\n')
|
||||
|
||||
# Environment checks
|
||||
if not sys.version_info >= (3, 5):
|
||||
error('Requires Python >= 3.5')
|
||||
|
||||
if 'ANDROID_HOME' not in os.environ:
|
||||
error('Please add Android SDK path to ANDROID_HOME environment variable!')
|
||||
|
||||
try:
|
||||
subprocess.run(['java', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except FileNotFoundError:
|
||||
error('Please install Java and make sure \'java\' is available in PATH')
|
||||
|
||||
# If not Windows, we need gcc to compile
|
||||
if os.name != 'nt':
|
||||
try:
|
||||
subprocess.run(['gcc', '-v'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except FileNotFoundError:
|
||||
error('Please install C compiler and make sure \'gcc\' is available in PATH')
|
||||
|
||||
import argparse
|
||||
import multiprocessing
|
||||
import zipfile
|
||||
import datetime
|
||||
import errno
|
||||
|
||||
def silentremove(file):
|
||||
try:
|
||||
os.remove(file)
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
|
||||
def zip_with_msg(zipfile, source, target):
|
||||
if not os.path.exists(source):
|
||||
error('{} does not exist! Try build \'binary\' and \'apk\' before zipping!'.format(source))
|
||||
print('zip: ' + source + ' -> ' + target)
|
||||
zipfile.write(source, target)
|
||||
|
||||
def build_all(args):
|
||||
build_binary(args)
|
||||
build_apk(args)
|
||||
zip_main(args)
|
||||
zip_uninstaller(args)
|
||||
|
||||
def build_binary(args):
|
||||
header('* Building Magisk binaries')
|
||||
|
||||
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
|
||||
debug_flag = '' if args.release else '-DDEBUG'
|
||||
proc = subprocess.run('{} APP_CFLAGS=\"-DMAGISK_VERSION=\\\"{}\\\" -DMAGISK_VER_CODE={} {}\" -j{}'.format(
|
||||
ndk_build, args.versionString, args.versionCode, debug_flag, multiprocessing.cpu_count()), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk binary failed!')
|
||||
|
||||
def build_apk(args):
|
||||
header('* Building Magisk Manager')
|
||||
|
||||
os.chdir('MagiskManager')
|
||||
if args.release:
|
||||
if not os.path.exists(os.path.join('..', 'release_signature.jks')):
|
||||
error('Please generate a java keystore and place it in \'release_signature.jks\'')
|
||||
|
||||
proc = subprocess.run('{} assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk Manager failed!')
|
||||
|
||||
unsigned = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release-unsigned.apk')
|
||||
aligned = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release-aligned.apk')
|
||||
release = os.path.join('app', 'build', 'outputs', 'apk', 'release', 'app-release.apk')
|
||||
|
||||
# Find the latest build tools
|
||||
build_tool = sorted(os.listdir(os.path.join(os.environ['ANDROID_HOME'], 'build-tools')))[-1]
|
||||
|
||||
silentremove(aligned)
|
||||
silentremove(release)
|
||||
|
||||
proc = subprocess.run([
|
||||
os.path.join(os.environ['ANDROID_HOME'], 'build-tools', build_tool, 'zipalign'),
|
||||
'-v', '-p', '4', unsigned, aligned])
|
||||
if proc.returncode != 0:
|
||||
error('Zipalign Magisk Manager failed!')
|
||||
|
||||
proc = subprocess.run('{} sign --ks {} --out {} {}'.format(
|
||||
'java -jar {}'.format(os.path.join('../ziptools/apksigner.jar')),
|
||||
os.path.join('..', 'release_signature.jks'),
|
||||
release, aligned), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Release sign Magisk Manager failed!')
|
||||
|
||||
silentremove(unsigned)
|
||||
silentremove(aligned)
|
||||
else:
|
||||
proc = subprocess.run('{} assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build Magisk Manager failed!')
|
||||
|
||||
# Return to upper directory
|
||||
os.chdir('..')
|
||||
|
||||
def sign_adjust_zip(unsigned, output):
|
||||
header('* Signing / Adjusting Zip')
|
||||
|
||||
# Unsigned->signed
|
||||
proc = subprocess.run(['java', '-jar', os.path.join('ziptools', 'signapk.jar'),
|
||||
os.path.join('ziptools', 'test.certificate.x509.pem'),
|
||||
os.path.join('ziptools', 'test.key.pk8'), unsigned, 'tmp_signed.zip'])
|
||||
if proc.returncode != 0:
|
||||
error('First sign flashable zip failed!')
|
||||
|
||||
if os.name != 'nt' and not os.path.exists(os.path.join('ziptools', 'zipadjust')):
|
||||
# Compile zipadjust
|
||||
proc = subprocess.run('gcc -o ziptools/zipadjust ziptools/src/*.c -lz', shell=True)
|
||||
if proc.returncode != 0:
|
||||
error('Build zipadjust failed!')
|
||||
|
||||
# Adjust zip
|
||||
proc = subprocess.run([os.path.join('ziptools', 'zipadjust'), 'tmp_signed.zip', 'tmp_adjusted.zip'])
|
||||
if proc.returncode != 0:
|
||||
error('Adjust flashable zip failed!')
|
||||
|
||||
# Adjusted -> output
|
||||
proc = subprocess.run(['java', '-jar', os.path.join('ziptools', 'minsignapk.jar'),
|
||||
os.path.join('ziptools', 'test.certificate.x509.pem'),
|
||||
os.path.join('ziptools', 'test.key.pk8'), 'tmp_adjusted.zip', output])
|
||||
if proc.returncode != 0:
|
||||
error('Second sign flashable zip failed!')
|
||||
|
||||
# Cleanup
|
||||
silentremove(unsigned)
|
||||
silentremove('tmp_signed.zip')
|
||||
silentremove('tmp_adjusted.zip')
|
||||
|
||||
def zip_main(args):
|
||||
header('* Packing Flashable Zip')
|
||||
|
||||
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zipf:
|
||||
# Compiled Binaries
|
||||
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
|
||||
for binary in ['magisk', 'magiskboot']:
|
||||
source = os.path.join('libs', lib_dir, binary)
|
||||
target = os.path.join(zip_dir, binary)
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# APK
|
||||
source = os.path.join('MagiskManager', 'app', 'build', 'outputs', 'apk',
|
||||
'release' if args.release else 'debug', 'app-release.apk' if args.release else 'app-debug.apk')
|
||||
target = os.path.join('common', 'magisk.apk')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# Scripts
|
||||
# flash_script.sh
|
||||
source = os.path.join('scripts', 'flash_script.sh')
|
||||
with open(source, 'r') as script:
|
||||
# Add version info into flash script
|
||||
update_binary = script.read().replace(
|
||||
'MAGISK_VERSION_STUB', 'Magisk v{} Installer'.format(args.versionString))
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
|
||||
print('zip: ' + source + ' -> ' + target)
|
||||
zipf.writestr(target, update_binary)
|
||||
# addon.d.sh
|
||||
source = os.path.join('scripts', 'addon.d.sh')
|
||||
with open(source, 'r') as script:
|
||||
# Add version info addon.d.sh
|
||||
addond = script.read().replace(
|
||||
'MAGISK_VERSION_STUB', 'Magisk v{} addon.d'.format(args.versionString))
|
||||
target = os.path.join('addon.d', '99-magisk.sh')
|
||||
print('zip: ' + source + ' -> ' + target)
|
||||
zipf.writestr(target, addond)
|
||||
# updater-script
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
|
||||
print('zip: ' + target)
|
||||
zipf.writestr(target, '#MAGISK\n')
|
||||
# init.magisk.rc
|
||||
source = os.path.join('scripts', 'init.magisk.rc')
|
||||
target = os.path.join('common', 'init.magisk.rc')
|
||||
zip_with_msg(zipf, source, target)
|
||||
# boot_patch.sh
|
||||
source = os.path.join('scripts', 'boot_patch.sh')
|
||||
target = os.path.join('common', 'boot_patch.sh')
|
||||
zip_with_msg(zipf, source, target)
|
||||
# util_functions.sh
|
||||
source = os.path.join('scripts', 'util_functions.sh')
|
||||
with open(source, 'r') as script:
|
||||
# Add version info util_functions.sh
|
||||
util_func = script.read().replace(
|
||||
'MAGISK_VERSION_STUB', 'SCRIPT_VERSION={}'.format(args.versionCode))
|
||||
target = os.path.join('common', 'util_functions.sh')
|
||||
print('zip: ' + source + ' -> ' + target)
|
||||
zipf.writestr(target, util_func)
|
||||
# Prebuilts
|
||||
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
||||
source = os.path.join('chromeos', chromeos)
|
||||
zip_with_msg(zipf, source, source)
|
||||
|
||||
# End of zipping
|
||||
|
||||
output = 'Magisk-v{}.zip'.format(args.versionString)
|
||||
sign_adjust_zip('tmp_unsigned.zip', output)
|
||||
|
||||
def zip_uninstaller(args):
|
||||
header('* Packing Uninstaller Zip')
|
||||
|
||||
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zipf:
|
||||
# Compiled Binaries
|
||||
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
|
||||
source = os.path.join('libs', lib_dir, 'magiskboot')
|
||||
target = os.path.join(zip_dir, 'magiskboot')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
source = os.path.join('scripts', 'magisk_uninstaller.sh')
|
||||
target = 'magisk_uninstaller.sh'
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# util_functions.sh
|
||||
source = os.path.join('scripts', 'util_functions.sh')
|
||||
with open(source, 'r') as script:
|
||||
# Remove the stub
|
||||
util_func = script.read().replace(
|
||||
'MAGISK_VERSION_STUB', '')
|
||||
target = os.path.join('util_functions.sh')
|
||||
print('zip: ' + source + ' -> ' + target)
|
||||
zipf.writestr(target, util_func)
|
||||
|
||||
source = os.path.join('scripts', 'uninstaller_loader.sh')
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
|
||||
print('zip: ' + target)
|
||||
zipf.writestr(target, '#MAGISK\n')
|
||||
|
||||
# Prebuilts
|
||||
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
||||
source = os.path.join('chromeos', chromeos)
|
||||
zip_with_msg(zipf, source, source)
|
||||
|
||||
# End of zipping
|
||||
|
||||
output = 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d'))
|
||||
sign_adjust_zip('tmp_unsigned.zip', output)
|
||||
|
||||
def cleanup(args):
|
||||
if len(args.target) == 0:
|
||||
args.target = ['binary', 'apk', 'zip']
|
||||
|
||||
if 'binary' in args.target:
|
||||
header('* Cleaning Magisk binaries')
|
||||
subprocess.run(os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') + ' clean', shell=True)
|
||||
|
||||
if 'apk' in args.target:
|
||||
header('* Cleaning Magisk Manager')
|
||||
os.chdir('MagiskManager')
|
||||
subprocess.run('{} clean'.format(os.path.join('.', 'gradlew')), shell=True)
|
||||
os.chdir('..')
|
||||
|
||||
if 'zip' in args.target:
|
||||
header('* Cleaning created zip files')
|
||||
for f in os.listdir('.'):
|
||||
if '.zip' in f:
|
||||
print('rm {}'.format(f))
|
||||
silentremove(f)
|
||||
|
||||
parser = argparse.ArgumentParser(description='Magisk build script')
|
||||
parser.add_argument('--release', action='store_true', help='compile Magisk for release')
|
||||
subparsers = parser.add_subparsers(title='actions')
|
||||
|
||||
all_parser = subparsers.add_parser('all', help='build everything and create flashable zip with uninstaller')
|
||||
all_parser.add_argument('versionString')
|
||||
all_parser.add_argument('versionCode', type=int)
|
||||
all_parser.set_defaults(func=build_all)
|
||||
|
||||
binary_parser = subparsers.add_parser('binary', help='build Magisk binaries')
|
||||
binary_parser.add_argument('versionString')
|
||||
binary_parser.add_argument('versionCode', type=int)
|
||||
binary_parser.set_defaults(func=build_binary)
|
||||
|
||||
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK')
|
||||
apk_parser.set_defaults(func=build_apk)
|
||||
|
||||
zip_parser = subparsers.add_parser('zip', help='zip and sign Magisk into a flashable zip')
|
||||
zip_parser.add_argument('versionString')
|
||||
zip_parser.add_argument('versionCode', type=int)
|
||||
zip_parser.set_defaults(func=zip_main)
|
||||
|
||||
uninstaller_parser = subparsers.add_parser('uninstaller', help='create flashable uninstaller')
|
||||
uninstaller_parser.set_defaults(func=zip_uninstaller)
|
||||
|
||||
clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary apk zip')
|
||||
clean_parser.add_argument('target', nargs='*')
|
||||
clean_parser.set_defaults(func=cleanup)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
args.func(args)
|
||||
156
build.sh
156
build.sh
@@ -1,156 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
usage() {
|
||||
echo "$0 all <version name>"
|
||||
echo -e "\tBuild binaries, zip, and sign Magisk"
|
||||
echo -e "\tThis is equlivant to first <build>, then <zip>"
|
||||
echo "$0 clean"
|
||||
echo -e "\tCleanup compiled / generated files"
|
||||
echo "$0 build"
|
||||
echo -e "\tBuild the binaries with ndk"
|
||||
echo "$0 zip <version name>"
|
||||
echo -e "\tZip and sign Magisk"
|
||||
echo "$0 uninstaller"
|
||||
echo -e "\tZip and sign the uninstaller"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
echo "************************"
|
||||
echo "* Cleaning up"
|
||||
echo "************************"
|
||||
ndk-build clean 2>/dev/null
|
||||
ls zip_static/arm/* | grep -v "busybox" | xargs rm -rfv
|
||||
ls zip_static/arm64/* | grep -v "busybox" | xargs rm -rfv
|
||||
ls zip_static/x86/* | grep -v "busybox" | xargs rm -rfv
|
||||
ls zip_static/x64/* | grep -v "busybox" | xargs rm -rfv
|
||||
rm -rfv zip_static/META-INF/com/google/android/update-binary
|
||||
rm -rfv zip_static/common/custom_ramdisk_patch.sh
|
||||
rm -rfv zip_static/common/magisksu.sh
|
||||
rm -rfv zip_static/common/init.magisk.rc
|
||||
rm -rfv zip_static/common/magic_mask.sh
|
||||
rm -rfv uninstaller/common
|
||||
rm -rfv uninstaller/arm
|
||||
rm -rfv uninstaller/arm64
|
||||
rm -rfv uninstaller/x86
|
||||
rm -rfv uninstaller/x64
|
||||
}
|
||||
|
||||
mkcp() {
|
||||
[ ! -d "$2" ] && mkdir -p "$2"
|
||||
cp -afv $1 $2
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "\n! $1\n"
|
||||
exit 1
|
||||
}
|
||||
|
||||
build_bin() {
|
||||
echo "************************"
|
||||
echo "* Building binaries"
|
||||
echo "************************"
|
||||
[ -z `which ndk-build` ] && error "Please add ndk-build to PATH!"
|
||||
ndk-build -j4 || error "Magisk binary tools build failed...."
|
||||
echo "************************"
|
||||
echo "* Copying binaries"
|
||||
echo "************************"
|
||||
mkcp "libs/armeabi-v7a/*" zip_static/arm
|
||||
mkcp libs/armeabi-v7a/bootimgtools uninstaller/arm
|
||||
mkcp "libs/arm64-v8a/*" zip_static/arm64
|
||||
mkcp libs/arm64-v8a/bootimgtools uninstaller/arm64
|
||||
mkcp "libs/x86/*" zip_static/x86
|
||||
mkcp libs/x86/bootimgtools uninstaller/x86
|
||||
mkcp "libs/x86_64/*" zip_static/x64
|
||||
mkcp libs/x86_64/bootimgtools uninstaller/x64
|
||||
}
|
||||
|
||||
zip_package() {
|
||||
[ ! -f "zip_static/arm/bootimgtools" ] && error "Missing binaries!! Please run '$0 build' before zipping"
|
||||
echo "************************"
|
||||
echo "* Adding version info"
|
||||
echo "************************"
|
||||
sed "s/MAGISK_VERSION_STUB/Magisk v$1 Boot Image Patcher/g" scripts/flash_script.sh > zip_static/META-INF/com/google/android/update-binary
|
||||
sed "s/MAGISK_VERSION_STUB/setprop magisk.version \"$1\"/g" scripts/magic_mask.sh > zip_static/common/magic_mask.sh
|
||||
echo "************************"
|
||||
echo "* Zipping Magisk v$1"
|
||||
echo "************************"
|
||||
cp -afv scripts/custom_ramdisk_patch.sh zip_static/common/custom_ramdisk_patch.sh
|
||||
cp -afv scripts/magisksu.sh zip_static/common/magisksu.sh
|
||||
cp -afv scripts/init.magisk.rc zip_static/common/init.magisk.rc
|
||||
cd zip_static
|
||||
find . -type f -exec chmod 644 {} \;
|
||||
find . -type d -exec chmod 755 {} \;
|
||||
rm -rf "../Magisk-v$1.zip"
|
||||
zip "../Magisk-v$1.zip" -r .
|
||||
cd ../
|
||||
sign_zip "Magisk-v$1.zip"
|
||||
}
|
||||
|
||||
zip_uninstaller() {
|
||||
[ ! -f "uninstaller/arm/bootimgtools" ] && error "Missing binaries!! Please run '$0 build' before zipping"
|
||||
echo "************************"
|
||||
echo "* Zipping uninstaller"
|
||||
echo "************************"
|
||||
mkcp scripts/magisk_uninstaller.sh uninstaller/common
|
||||
cd uninstaller
|
||||
find . -type f -exec chmod 644 {} \;
|
||||
find . -type d -exec chmod 755 {} \;
|
||||
TIMESTAMP=`date "+%Y%m%d"`
|
||||
rm -rf "../Magisk-uninstaller-$TIMESTAMP.zip"
|
||||
zip "../Magisk-uninstaller-$TIMESTAMP.zip" -r .
|
||||
cd ../
|
||||
sign_zip "Magisk-uninstaller-$TIMESTAMP.zip"
|
||||
}
|
||||
|
||||
sign_zip() {
|
||||
if [ ! -f "ziptools/zipadjust" ]; then
|
||||
echo "************************"
|
||||
echo "* Compiling ZipAdjust"
|
||||
echo "************************"
|
||||
gcc -o ziptools/zipadjust ziptools/src/*.c -lz || error "ZipAdjust Build failed...."
|
||||
chmod 755 ziptools/zipadjust
|
||||
fi
|
||||
echo "************************"
|
||||
echo "* First sign $1"
|
||||
echo "************************"
|
||||
java -jar "ziptools/signapk.jar" "ziptools/test.certificate.x509.pem" "ziptools/test.key.pk8" "$1" "${1%.*}-firstsign.zip"
|
||||
echo "************************"
|
||||
echo "* Adjusting $1"
|
||||
echo "************************"
|
||||
ziptools/zipadjust "${1%.*}-firstsign.zip" "${1%.*}-adjusted.zip"
|
||||
echo "************************"
|
||||
echo "* Final sign $1"
|
||||
echo "************************"
|
||||
java -jar "ziptools/minsignapk.jar" "ziptools/test.certificate.x509.pem" "ziptools/test.key.pk8" "${1%.*}-adjusted.zip" "${1%.*}-signed.zip"
|
||||
|
||||
mv "${1%.*}-signed.zip" "$1"
|
||||
rm "${1%.*}-adjusted.zip" "${1%.*}-firstsign.zip"
|
||||
}
|
||||
|
||||
DIR="$(cd "$(dirname "$0")"; pwd)"
|
||||
cd "$DIR"
|
||||
|
||||
case $1 in
|
||||
"all" )
|
||||
[ -z "$2" ] && echo -e "! Missing version number\n" && usage
|
||||
build_bin
|
||||
zip_package $2
|
||||
;;
|
||||
"clean" )
|
||||
cleanup
|
||||
;;
|
||||
"build" )
|
||||
build_bin
|
||||
;;
|
||||
"zip" )
|
||||
[ -z "$2" ] && echo -e "! Missing version number\n" && usage
|
||||
zip_package $2
|
||||
;;
|
||||
"uninstaller" )
|
||||
zip_uninstaller
|
||||
;;
|
||||
* )
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
@@ -1,10 +1,56 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include jni/bootimgtools/Android.mk
|
||||
include jni/magiskhide/Android.mk
|
||||
include jni/resetprop/Android.mk
|
||||
include jni/sepolicy-inject/Android.mk
|
||||
include jni/su/Android.mk
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magisk
|
||||
LOCAL_STATIC_LIBRARIES := libsepol
|
||||
LOCAL_SHARED_LIBRARIES := libsqlite libselinux
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
$(LOCAL_PATH)/utils \
|
||||
$(LOCAL_PATH)/daemon \
|
||||
$(LOCAL_PATH)/resetprop \
|
||||
$(LOCAL_PATH)/magiskpolicy \
|
||||
$(LOCAL_PATH)/external \
|
||||
$(LOCAL_PATH)/selinux/libsepol/include
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
main.c \
|
||||
utils/misc.c \
|
||||
utils/vector.c \
|
||||
utils/xwrap.c \
|
||||
utils/list.c \
|
||||
utils/img.c \
|
||||
daemon/daemon.c \
|
||||
daemon/socket_trans.c \
|
||||
daemon/log_monitor.c \
|
||||
daemon/bootstages.c \
|
||||
magiskhide/magiskhide.c \
|
||||
magiskhide/proc_monitor.c \
|
||||
magiskhide/hide_utils.c \
|
||||
magiskpolicy/magiskpolicy.c \
|
||||
magiskpolicy/rules.c \
|
||||
magiskpolicy/sepolicy.c \
|
||||
magiskpolicy/api.c \
|
||||
resetprop/resetprop.cpp \
|
||||
resetprop/system_properties.cpp \
|
||||
su/su.c \
|
||||
su/activity.c \
|
||||
su/db.c \
|
||||
su/misc.c \
|
||||
su/pts.c \
|
||||
su/su_daemon.c \
|
||||
su/su_socket.c
|
||||
|
||||
LOCAL_CFLAGS := -Wno-implicit-exception-spec-mismatch
|
||||
LOCAL_LDLIBS := -llog
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# External shared libraries, build stub libraries for linking
|
||||
include jni/external/Android.mk
|
||||
|
||||
# libsepol, static library
|
||||
include jni/selinux/libsepol/Android.mk
|
||||
include jni/selinux/libselinux/Android.mk
|
||||
|
||||
# Build magiskboot
|
||||
include jni/magiskboot/Android.mk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
APP_ABI := x86 x86_64 armeabi-v7a arm64-v8a
|
||||
APP_PIE = true
|
||||
APP_PLATFORM := android-21
|
||||
APP_UNIFIED_HEADERS := true
|
||||
APP_CPPFLAGS += -std=c++11
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := bootimgtools
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_SRC_FILES := main.c extract.c repack.c hexpatch.c
|
||||
LOCAL_CFLAGS += -std=gnu11
|
||||
include $(BUILD_EXECUTABLE)
|
||||
@@ -1,149 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bootimg.h"
|
||||
|
||||
void dump(uint8_t *ptr, size_t size, char* filename) {
|
||||
unlink(filename);
|
||||
int ofd = open(filename, O_WRONLY|O_CREAT, 0644);
|
||||
assert(ofd >= 0);
|
||||
int ret = write(ofd, ptr, size);
|
||||
assert(ret == size);
|
||||
close(ofd);
|
||||
}
|
||||
|
||||
//TODO: Search for other header types
|
||||
void dump_ramdisk(uint8_t *ptr, size_t size) {
|
||||
//GZip header
|
||||
if(memcmp(ptr, "\x1f\x8b\x08\x00", 4) == 0) {
|
||||
dump(ptr, size, "ramdisk.gz");
|
||||
//MTK header
|
||||
} else if(memcmp(ptr, "\x88\x16\x88\x58", 4) == 0) {
|
||||
if(memcmp(ptr+8, "RECOVERY", 8)==0) {
|
||||
dump(ptr, 0, "ramdisk-mtk-recovery");
|
||||
} else if(memcmp(ptr+8, "ROOTFS\0\0", 8)==0) {
|
||||
dump(ptr, 0, "ramdisk-mtk-boot");
|
||||
} else {
|
||||
exit(1);
|
||||
}
|
||||
dump(ptr, 0, "ramdisk-mtk"); //Create an mtk flag
|
||||
dump_ramdisk(ptr+512, size-512);
|
||||
} else {
|
||||
//Since our first aim is to extract/repack ramdisk
|
||||
//Stop if we can't find it
|
||||
//Still dump it for debug purposes
|
||||
dump(ptr, size, "ramdisk");
|
||||
|
||||
fprintf(stderr, "Unknown ramdisk type\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void search_security_hdr(uint8_t *buf, size_t size) {
|
||||
if(memcmp(buf, "CHROMEOS", 8) == 0) {
|
||||
dump(buf, 0, "chromeos");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int search_security(uint8_t *buf, size_t size, int pos) {
|
||||
//Rockchip signature
|
||||
if(memcmp(buf+1024, "SIGN", 4) == 0) {
|
||||
//Rockchip signature AT LEAST means the bootloader will check the crc
|
||||
dump(buf, 0, "rkcrc"); //Create an flag to tell it
|
||||
|
||||
//And it's possible there is a security too
|
||||
return 1;
|
||||
}
|
||||
|
||||
//If we didn't parse the whole file, it is highly likely there is a boot signature
|
||||
if(pos < size) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - At the moment we dump kernel + ramdisk + second + DT, it's likely we only want ramdisk
|
||||
* - Error-handling via assert() is perhaps not the best
|
||||
*/
|
||||
int extract(char *image) {
|
||||
|
||||
int fd = open(image, O_RDONLY);
|
||||
off_t size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
uint8_t *orig = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
uint8_t *base = orig;
|
||||
assert(base);
|
||||
|
||||
search_security_hdr(base, size);
|
||||
|
||||
//We're searching for the header in the whole file, we could stop earlier.
|
||||
//At least HTC and nVidia have a signature header
|
||||
while(base<(orig+size)) {
|
||||
if(memcmp(base, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0)
|
||||
break;
|
||||
//We're searching every 256bytes, is it ok?
|
||||
base += 256;
|
||||
}
|
||||
assert(base < (orig+size));
|
||||
|
||||
struct boot_img_hdr *hdr = (struct boot_img_hdr*) base;
|
||||
assert(
|
||||
hdr->page_size == 2048 ||
|
||||
hdr->page_size == 4096 ||
|
||||
hdr->page_size == 16384
|
||||
);
|
||||
|
||||
long pos = hdr->page_size;
|
||||
dump(base+pos, hdr->kernel_size, "kernel");
|
||||
pos += hdr->kernel_size + hdr->page_size-1;
|
||||
pos &= ~(hdr->page_size-1L);
|
||||
|
||||
dump_ramdisk(base+pos, hdr->ramdisk_size);
|
||||
pos += hdr->ramdisk_size + hdr->page_size-1;
|
||||
pos &= ~(hdr->page_size-1L);
|
||||
|
||||
if(hdr->second_size) {
|
||||
assert( (pos+hdr->second_size) <= size);
|
||||
dump(base+pos, hdr->second_size, "second");
|
||||
pos += hdr->second_size + hdr->page_size-1;
|
||||
pos &= ~(hdr->page_size-1L);
|
||||
}
|
||||
|
||||
//This is non-standard, so we triple check
|
||||
if( hdr->unused[0] &&
|
||||
pos < size &&
|
||||
(pos+hdr->unused[0]) <= size) {
|
||||
|
||||
if(memcmp(base+pos, "QCDT", 4) == 0 ||
|
||||
memcmp(base+pos, "SPRD", 4) == 0 ||
|
||||
memcmp(base+pos, "DTBH", 4) == 0 ||
|
||||
memcmp(base+pos, "\xD0\x0D\xFE\xED", 4) == 0
|
||||
) {
|
||||
dump(base+pos, hdr->unused[0], "dt");
|
||||
pos += hdr->unused[0] + hdr->page_size-1;
|
||||
pos &= ~(hdr->page_size-1L);
|
||||
}
|
||||
}
|
||||
|
||||
//If we think we find some security-related infos in the boot.img
|
||||
//create a "secure" flag to warn the user it is dangerous
|
||||
if(search_security(base, size, pos)) {
|
||||
dump(base, 0, "secure");
|
||||
}
|
||||
|
||||
munmap(orig, size);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bootimg.h"
|
||||
|
||||
int hex2int(char c) {
|
||||
int first = c / 16 - 3;
|
||||
int second = c % 16;
|
||||
int result = first * 10 + second;
|
||||
if(result > 9) result--;
|
||||
return result;
|
||||
}
|
||||
|
||||
int hex2ascii(char c, char d) {
|
||||
int high = hex2int(c) * 16;
|
||||
int low = hex2int(d);
|
||||
return high+low;
|
||||
}
|
||||
|
||||
void hexstr2str(char *hex, char *str) {
|
||||
char buf = 0;
|
||||
for(int i = 0, length = strlen(hex); i < length; ++i){
|
||||
if(i % 2){
|
||||
str[i / 2] = hex2ascii(buf, hex[i]);
|
||||
} else{
|
||||
buf = hex[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int hexpatch(char * image, char *from, char *to) {
|
||||
int fd = open(image, O_RDWR), patternsize = strlen(from) / 2, patchsize = strlen(to) / 2;
|
||||
off_t filesize = lseek(fd, 0, SEEK_END);
|
||||
char *file, *pattern, *patch, *start;
|
||||
file = malloc(sizeof (char) * filesize);
|
||||
pattern = malloc(sizeof (char) * patternsize);
|
||||
patch = malloc(sizeof (char) * patchsize);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
read(fd, file, filesize);
|
||||
hexstr2str(from, pattern);
|
||||
hexstr2str(to, patch);
|
||||
for (off_t i = 0; i < filesize;) {
|
||||
int j;
|
||||
for (j = 0; j < patternsize; ++j) {
|
||||
if(file[i + j] != pattern[j]) break;
|
||||
}
|
||||
if (j == patternsize) {
|
||||
fprintf(stderr, "Pattern %s found!\nPatching to %s\n", from, to);
|
||||
lseek(fd, i, SEEK_SET);
|
||||
write(fd, patch, patchsize);
|
||||
}
|
||||
if(j == 0) j = 1;
|
||||
i += j;
|
||||
}
|
||||
free(file);
|
||||
free(pattern);
|
||||
free(patch);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "bootimg.h"
|
||||
|
||||
/********************
|
||||
Patch Boot Image
|
||||
*********************/
|
||||
|
||||
int usage(char *arg0) {
|
||||
fprintf(stderr, "Boot Image Unpack/Repack Tool\n");
|
||||
fprintf(stderr, "%s --extract <bootimage>\n", arg0);
|
||||
fprintf(stderr, " Unpack <bootimage> into current directory\n\n");
|
||||
fprintf(stderr, "%s --repack <bootimage>\n", arg0);
|
||||
fprintf(stderr, " Repack kernel, dt, ramdisk... from current directory to new-image.img\n <bootimage> is the image you've just unpacked\n\n");
|
||||
fprintf(stderr, "%s --hexpatch <bootimage> <hexpattern1> <hexpattern2>\n", arg0);
|
||||
fprintf(stderr, " Search <hexpattern1> in <bootimage>, and replace with <hexpattern2>\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char ch;
|
||||
struct option long_options[] = {
|
||||
{"extract", required_argument, NULL, 'e'},
|
||||
{"repack", required_argument, NULL, 'r'},
|
||||
{"hexpatch", required_argument, NULL, 'p'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
while ((ch = getopt_long(argc, argv, "e:r:p:", long_options, NULL)) != -1) {
|
||||
switch (ch) {
|
||||
case 'e':
|
||||
return extract(optarg);
|
||||
case 'r':
|
||||
return repack(optarg);
|
||||
case 'p':
|
||||
if (argc < 5) return usage(argv[0]);
|
||||
optind += 2;
|
||||
return hexpatch(argv[optind - 3], argv[optind - 2], argv[optind - 1]);
|
||||
default:
|
||||
return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bootimg.h"
|
||||
|
||||
off_t file_size(char *filename) {
|
||||
struct stat st;
|
||||
if(stat(filename, &st))
|
||||
exit(1);
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
int append_file(int ofd, char *filename, off_t pos) {
|
||||
lseek(ofd, pos, SEEK_SET);
|
||||
int fd = open(filename, O_RDONLY);
|
||||
int size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
sendfile(ofd, fd, NULL, size);
|
||||
close(fd);
|
||||
return size;
|
||||
}
|
||||
|
||||
int append_ramdisk(int ofd, off_t pos) {
|
||||
if(access("ramdisk-mtk", R_OK) == 0) {
|
||||
char buf[512];
|
||||
off_t size = file_size("ramdisk.gz");
|
||||
memcpy(buf, "\x88\x16\x88\x58", 4);
|
||||
uint32_t v = size;
|
||||
memcpy(buf+4, &v, sizeof(v)); //Should convert to LE
|
||||
|
||||
//TODO: RECOVERY OR ROOTFS?
|
||||
char str[32];
|
||||
memset(str, 0, sizeof(str));
|
||||
if(access("ramdisk-mtk-boot", R_OK)==0) {
|
||||
strcpy(str, "ROOTFS");
|
||||
} else if(access("ramdisk-mtk-recovery", R_OK)==0) {
|
||||
strcpy(str, "RECOVERY");
|
||||
} else {
|
||||
exit(1);
|
||||
}
|
||||
memcpy(buf+8, str, sizeof(str));
|
||||
|
||||
memset(buf+8+sizeof(str), 0xff, 512-8-sizeof(str));
|
||||
|
||||
pwrite(ofd, buf, sizeof(buf), pos);
|
||||
|
||||
return append_file(ofd, "ramdisk.gz", pos + 512) + 512;
|
||||
} else if(access("ramdisk.gz", R_OK) == 0) {
|
||||
return append_file(ofd, "ramdisk.gz", pos);
|
||||
} else {
|
||||
return append_file(ofd, "ramdisk", pos);
|
||||
}
|
||||
}
|
||||
|
||||
void post_process(struct boot_img_hdr *hdr, int ofd, int pos) {
|
||||
if(access("rkcrc", R_OK) == 0) {
|
||||
fprintf(stderr, "Rockchip CRCs not supported yet\n");
|
||||
exit(1);
|
||||
}
|
||||
//Round up the file size
|
||||
ftruncate(ofd, pos);
|
||||
}
|
||||
|
||||
int repack(char *image) {
|
||||
|
||||
//TODO: Merge with extract.c?
|
||||
//{
|
||||
int ifd = open(image, O_RDONLY);
|
||||
off_t isize = lseek(ifd, 0, SEEK_END);
|
||||
lseek(ifd, 0, SEEK_SET);
|
||||
uint8_t *iorig = mmap(NULL, isize, PROT_READ, MAP_SHARED, ifd, 0);
|
||||
uint8_t *ibase = iorig;
|
||||
assert(ibase);
|
||||
|
||||
while(ibase<(iorig+isize)) {
|
||||
if(memcmp(ibase, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0)
|
||||
break;
|
||||
ibase += 256;
|
||||
}
|
||||
assert(ibase < (iorig+isize));
|
||||
//}
|
||||
//
|
||||
struct boot_img_hdr *ihdr = (struct boot_img_hdr*) ibase;
|
||||
assert(
|
||||
ihdr->page_size == 2048 ||
|
||||
ihdr->page_size == 4096 ||
|
||||
ihdr->page_size == 16384
|
||||
);
|
||||
|
||||
unlink("new-boot.img");
|
||||
int ofd = open("new-boot.img", O_RDWR|O_CREAT, 0644);
|
||||
ftruncate(ofd, ihdr->page_size);
|
||||
//Write back original header, we'll change it later
|
||||
write(ofd, ihdr, sizeof(*ihdr));
|
||||
|
||||
struct boot_img_hdr *hdr = mmap(NULL, sizeof(*ihdr), PROT_READ|PROT_WRITE, MAP_SHARED, ofd, 0);
|
||||
//First set everything to zero, so we know where we are at.
|
||||
hdr->kernel_size = 0;
|
||||
hdr->ramdisk_size = 0;
|
||||
hdr->second_size = 0;
|
||||
hdr->unused[0] = 0;
|
||||
memset(hdr->id, 0, sizeof(hdr->id)); //Setting id to 0 might be wrong?
|
||||
|
||||
int pos = hdr->page_size;
|
||||
int size = 0;
|
||||
|
||||
size = append_file(ofd, "kernel", pos);
|
||||
pos += size + hdr->page_size - 1;
|
||||
pos &= ~(hdr->page_size-1);
|
||||
hdr->kernel_size = size;
|
||||
|
||||
size = append_ramdisk(ofd, pos);
|
||||
pos += size + hdr->page_size - 1;
|
||||
pos &= ~(hdr->page_size-1);
|
||||
hdr->ramdisk_size = size;
|
||||
|
||||
if(access("second", R_OK) == 0) {
|
||||
size = append_file(ofd, "second", pos);
|
||||
pos += size + hdr->page_size - 1;
|
||||
pos &= ~(hdr->page_size-1);
|
||||
hdr->second_size = size;
|
||||
}
|
||||
|
||||
if(access("dt", R_OK) == 0) {
|
||||
size = append_file(ofd, "dt", pos);
|
||||
pos += size + hdr->page_size - 1;
|
||||
pos &= ~(hdr->page_size-1);
|
||||
hdr->unused[0] = size;
|
||||
}
|
||||
|
||||
post_process(hdr, ofd, pos);
|
||||
munmap(hdr, sizeof(*ihdr));
|
||||
close(ofd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
781
jni/daemon/bootstages.c
Normal file
781
jni/daemon/bootstages.c
Normal file
@@ -0,0 +1,781 @@
|
||||
/* bootstages.c - Core bootstage operations
|
||||
*
|
||||
* All bootstage operations, including simple mount in post-fs,
|
||||
* magisk mount in post-fs-data, various image handling, script
|
||||
* execution, load modules, install Magisk Manager etc.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <linux/loop.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
#include "resetprop.h"
|
||||
|
||||
static char *buf, *buf2;
|
||||
static struct vector module_list;
|
||||
|
||||
#ifdef DEBUG
|
||||
static int debug_log_pid, debug_log_fd;
|
||||
#endif
|
||||
|
||||
/******************
|
||||
* Node structure *
|
||||
******************/
|
||||
|
||||
// Precedence: MODULE > SKEL > INTER > DUMMY
|
||||
#define IS_DUMMY 0x01 /* mount from mirror */
|
||||
#define IS_INTER 0x02 /* intermediate node */
|
||||
#define IS_SKEL 0x04 /* mount from skeleton */
|
||||
#define IS_MODULE 0x08 /* mount from module */
|
||||
|
||||
#define IS_VENDOR 0x10 /* special vendor placeholder */
|
||||
|
||||
struct node_entry {
|
||||
const char *module; /* Only used when status & IS_MODULE */
|
||||
char *name;
|
||||
uint8_t type;
|
||||
uint8_t status;
|
||||
struct node_entry *parent;
|
||||
struct vector *children;
|
||||
};
|
||||
|
||||
#define IS_DIR(n) (n->type == DT_DIR)
|
||||
#define IS_LNK(n) (n->type == DT_LNK)
|
||||
#define IS_REG(n) (n->type == DT_REG)
|
||||
|
||||
/******************
|
||||
* Image handling *
|
||||
******************/
|
||||
|
||||
#define round_size(a) ((((a) / 32) + 2) * 32)
|
||||
#define SOURCE_TMP "/dev/source"
|
||||
#define TARGET_TMP "/dev/target"
|
||||
|
||||
static int merge_img(const char *source, const char *target) {
|
||||
if (access(source, F_OK) == -1)
|
||||
return 0;
|
||||
if (access(target, F_OK) == -1) {
|
||||
rename(source, target);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// resize target to worst case
|
||||
int s_used, s_total, t_used, t_total, n_total;
|
||||
get_img_size(source, &s_used, &s_total);
|
||||
get_img_size(target, &t_used, &t_total);
|
||||
n_total = round_size(s_used + t_used);
|
||||
if (n_total != t_total)
|
||||
resize_img(target, n_total);
|
||||
|
||||
xmkdir(SOURCE_TMP, 0755);
|
||||
xmkdir(TARGET_TMP, 0755);
|
||||
char *s_loop, *t_loop;
|
||||
s_loop = mount_image(source, SOURCE_TMP);
|
||||
if (s_loop == NULL) return 1;
|
||||
t_loop = mount_image(target, TARGET_TMP);
|
||||
if (t_loop == NULL) return 1;
|
||||
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
if (!(dir = opendir(SOURCE_TMP)))
|
||||
return 1;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (strcmp(entry->d_name, ".") == 0 ||
|
||||
strcmp(entry->d_name, "..") == 0 ||
|
||||
strcmp(entry->d_name, ".core") == 0 ||
|
||||
strcmp(entry->d_name, "lost+found") == 0)
|
||||
continue;
|
||||
// Cleanup old module if exists
|
||||
snprintf(buf, PATH_MAX, "%s/%s", TARGET_TMP, entry->d_name);
|
||||
if (access(buf, F_OK) == 0) {
|
||||
LOGI("Upgrade module: %s\n", entry->d_name);
|
||||
rm_rf(buf);
|
||||
} else {
|
||||
LOGI("New module: %s\n", entry->d_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
clone_dir(SOURCE_TMP, TARGET_TMP);
|
||||
|
||||
// Unmount all loop devices
|
||||
umount_image(SOURCE_TMP, s_loop);
|
||||
umount_image(TARGET_TMP, t_loop);
|
||||
rmdir(SOURCE_TMP);
|
||||
rmdir(TARGET_TMP);
|
||||
free(s_loop);
|
||||
free(t_loop);
|
||||
unlink(source);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void trim_img(const char *img) {
|
||||
int used, total, new_size;
|
||||
get_img_size(img, &used, &total);
|
||||
new_size = round_size(used);
|
||||
if (new_size != total)
|
||||
resize_img(img, new_size);
|
||||
}
|
||||
|
||||
/***********
|
||||
* Scripts *
|
||||
***********/
|
||||
|
||||
void exec_common_script(const char* stage) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
snprintf(buf, PATH_MAX, "%s/%s.d", COREDIR, stage);
|
||||
|
||||
if (!(dir = opendir(buf)))
|
||||
return;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_REG) {
|
||||
snprintf(buf2, PATH_MAX, "%s/%s", buf, entry->d_name);
|
||||
if (access(buf2, X_OK) == -1)
|
||||
continue;
|
||||
LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
|
||||
char *const command[] = { "sh", buf2, NULL };
|
||||
int pid = run_command(0, NULL, "/system/bin/sh", command);
|
||||
if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void exec_module_script(const char* stage) {
|
||||
char *module;
|
||||
vec_for_each(&module_list, module) {
|
||||
snprintf(buf, PATH_MAX, "%s/%s/%s.sh", MOUNTPOINT, module, stage);
|
||||
if (access(buf, F_OK) == -1)
|
||||
continue;
|
||||
LOGI("%s: exec [%s.sh]\n", module, stage);
|
||||
char *const command[] = { "sh", buf, NULL };
|
||||
int pid = run_command(0, NULL, "/system/bin/sh", command);
|
||||
if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***************
|
||||
* Magic Mount *
|
||||
***************/
|
||||
|
||||
static char *get_full_path(struct node_entry *node) {
|
||||
char buffer[PATH_MAX], temp[PATH_MAX];
|
||||
// Concat the paths
|
||||
struct node_entry *cur = node;
|
||||
strcpy(buffer, node->name);
|
||||
while (cur->parent) {
|
||||
strcpy(temp, buffer);
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s", cur->parent->name, temp);
|
||||
cur = cur->parent;
|
||||
}
|
||||
return strdup(buffer);
|
||||
}
|
||||
|
||||
// Free the node
|
||||
static void destroy_node(struct node_entry *node) {
|
||||
free(node->name);
|
||||
vec_destroy(node->children);
|
||||
free(node->children);
|
||||
free(node);
|
||||
}
|
||||
|
||||
// Free the node and all children recursively
|
||||
static void destroy_subtree(struct node_entry *node) {
|
||||
// Never free parent, since it shall be freed by themselves
|
||||
struct node_entry *e;
|
||||
vec_for_each(node->children, e) {
|
||||
destroy_subtree(e);
|
||||
}
|
||||
destroy_node(node);
|
||||
}
|
||||
|
||||
// Return the child
|
||||
static struct node_entry *insert_child(struct node_entry *p, struct node_entry *c) {
|
||||
c->parent = p;
|
||||
if (p->children == NULL) {
|
||||
p->children = xmalloc(sizeof(struct vector));
|
||||
vec_init(p->children);
|
||||
}
|
||||
struct node_entry *e;
|
||||
vec_for_each(p->children, e) {
|
||||
if (strcmp(e->name, c->name) == 0) {
|
||||
// Exist duplicate
|
||||
if (c->status > e->status) {
|
||||
// Precedence is higher, replace with new node
|
||||
destroy_subtree(e);
|
||||
vec_entry(p->children)[_] = c;
|
||||
return c;
|
||||
} else {
|
||||
// Free the new entry, return old
|
||||
destroy_node(c);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
// New entry, push back
|
||||
vec_push_back(p->children, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
static void construct_tree(const char *module, struct node_entry *parent) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
struct node_entry *node;
|
||||
|
||||
char *parent_path = get_full_path(parent);
|
||||
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, module, parent_path);
|
||||
|
||||
if (!(dir = opendir(buf)))
|
||||
goto cleanup;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
// Create new node
|
||||
node = xcalloc(sizeof(*node), 1);
|
||||
node->module = module;
|
||||
node->name = strdup(entry->d_name);
|
||||
node->type = entry->d_type;
|
||||
snprintf(buf, PATH_MAX, "%s/%s", parent_path, node->name);
|
||||
|
||||
/*
|
||||
* Clone the parent in the following condition:
|
||||
* 1. File in module is a symlink
|
||||
* 2. Target file do not exist
|
||||
* 3. Target file is a symlink, but not /system/vendor
|
||||
*/
|
||||
int clone = 0;
|
||||
if (IS_LNK(node) || access(buf, F_OK) == -1) {
|
||||
clone = 1;
|
||||
} else if (strcmp(parent->name, "/system") != 0 || strcmp(node->name, "vendor") != 0) {
|
||||
struct stat s;
|
||||
xstat(buf, &s);
|
||||
if (S_ISLNK(s.st_mode))
|
||||
clone = 1;
|
||||
}
|
||||
|
||||
if (clone) {
|
||||
// Mark the parent folder as a skeleton
|
||||
parent->status |= IS_SKEL; /* This will not overwrite if parent is module */
|
||||
node->status = IS_MODULE;
|
||||
} else if (IS_DIR(node)) {
|
||||
// Check if marked as replace
|
||||
snprintf(buf2, PATH_MAX, "%s/%s%s/.replace", MOUNTPOINT, module, buf);
|
||||
if (access(buf2, F_OK) == 0) {
|
||||
// Replace everything, mark as leaf
|
||||
node->status = IS_MODULE;
|
||||
} else {
|
||||
// This will be an intermediate node
|
||||
node->status = IS_INTER;
|
||||
}
|
||||
} else if (IS_REG(node)) {
|
||||
// This is a leaf, mark as target
|
||||
node->status = IS_MODULE;
|
||||
}
|
||||
node = insert_child(parent, node);
|
||||
if (node->status & (IS_SKEL | IS_INTER)) {
|
||||
// Intermediate folder, travel deeper
|
||||
construct_tree(module, node);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
cleanup:
|
||||
free(parent_path);
|
||||
}
|
||||
|
||||
static void clone_skeleton(struct node_entry *node) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
struct node_entry *dummy, *child;
|
||||
|
||||
// Clone the structure
|
||||
char *full_path = get_full_path(node);
|
||||
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path);
|
||||
if (!(dir = opendir(buf)))
|
||||
goto cleanup;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
// Create dummy node
|
||||
dummy = xcalloc(sizeof(*dummy), 1);
|
||||
dummy->name = strdup(entry->d_name);
|
||||
dummy->type = entry->d_type;
|
||||
dummy->status = IS_DUMMY;
|
||||
insert_child(node, dummy);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
snprintf(buf, PATH_MAX, "%s%s", DUMMDIR, full_path);
|
||||
mkdir_p(buf, 0755);
|
||||
clone_attr(full_path, buf);
|
||||
if (node->status & IS_SKEL)
|
||||
bind_mount(buf, full_path);
|
||||
|
||||
vec_for_each(node->children, child) {
|
||||
snprintf(buf, PATH_MAX, "%s%s/%s", DUMMDIR, full_path, child->name);
|
||||
|
||||
// Create the dummy file/directory
|
||||
if (IS_DIR(child))
|
||||
xmkdir(buf, 0755);
|
||||
else if (IS_REG(child))
|
||||
close(open_new(buf));
|
||||
// Links will be handled later
|
||||
|
||||
if (child->status & IS_VENDOR) {
|
||||
if (IS_LNK(child)) {
|
||||
cp_afc(MIRRDIR "/system/vendor", "/system/vendor");
|
||||
LOGI("cplink: %s -> %s\n", MIRRDIR "/system/vendor", "/system/vendor");
|
||||
}
|
||||
// Skip
|
||||
continue;
|
||||
} else if (child->status & IS_MODULE) {
|
||||
// Mount from module file to dummy file
|
||||
snprintf(buf2, PATH_MAX, "%s/%s%s/%s", MOUNTPOINT, child->module, full_path, child->name);
|
||||
} else if (child->status & (IS_SKEL | IS_INTER)) {
|
||||
// It's a intermediate folder, recursive clone
|
||||
clone_skeleton(child);
|
||||
continue;
|
||||
} else if (child->status & IS_DUMMY) {
|
||||
// Mount from mirror to dummy file
|
||||
snprintf(buf2, PATH_MAX, "%s%s/%s", MIRRDIR, full_path, child->name);
|
||||
}
|
||||
|
||||
if (IS_LNK(child)) {
|
||||
// Copy symlinks directly
|
||||
cp_afc(buf2, buf);
|
||||
LOGI("cplink: %s -> %s\n", buf2, buf);
|
||||
} else {
|
||||
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
|
||||
bind_mount(buf2, buf);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(full_path);
|
||||
}
|
||||
|
||||
static void magic_mount(struct node_entry *node) {
|
||||
char *real_path;
|
||||
struct node_entry *child;
|
||||
|
||||
if (node->status & IS_MODULE) {
|
||||
// The real deal, mount module item
|
||||
real_path = get_full_path(node);
|
||||
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, node->module, real_path);
|
||||
bind_mount(buf, real_path);
|
||||
free(real_path);
|
||||
} else if (node->status & IS_SKEL) {
|
||||
// The node is labeled to be cloned with skeleton, lets do it
|
||||
clone_skeleton(node);
|
||||
} else if (node->status & IS_INTER) {
|
||||
// It's an intermediate node, travel deeper
|
||||
vec_for_each(node->children, child)
|
||||
magic_mount(child);
|
||||
}
|
||||
// The only thing goes here should be vendor placeholder
|
||||
// There should be no dummies, so don't need to handle it here
|
||||
}
|
||||
|
||||
/****************
|
||||
* Simple Mount *
|
||||
****************/
|
||||
|
||||
static void simple_mount(const char *path) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
snprintf(buf, PATH_MAX, "%s%s", CACHEMOUNT, path);
|
||||
if (!(dir = opendir(buf)))
|
||||
return;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
// Target file path
|
||||
snprintf(buf2, PATH_MAX, "%s/%s", path, entry->d_name);
|
||||
// Only mount existing file
|
||||
if (access(buf2, F_OK) == -1)
|
||||
continue;
|
||||
if (entry->d_type == DT_DIR) {
|
||||
char *new_path = strdup(buf2);
|
||||
simple_mount(new_path);
|
||||
free(new_path);
|
||||
} else if (entry->d_type == DT_REG) {
|
||||
// Actual file path
|
||||
snprintf(buf, PATH_MAX, "%s%s", CACHEMOUNT, buf2);
|
||||
// Clone all attributes
|
||||
clone_attr(buf2, buf);
|
||||
// Finally, mount the file
|
||||
bind_mount(buf, buf2);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
/****************
|
||||
* Entry points *
|
||||
****************/
|
||||
|
||||
static void *start_magisk_hide(void *args) {
|
||||
launch_magiskhide(-1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void unblock_boot_process() {
|
||||
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void post_fs(int client) {
|
||||
// Error handler
|
||||
err_handler = unblock_boot_process;
|
||||
|
||||
// Start log monitor
|
||||
monitor_logs();
|
||||
|
||||
LOGI("** post-fs mode running\n");
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
|
||||
// Uninstall or core only mode
|
||||
if (access(UNINSTALLER, F_OK) == 0 || access(DISABLEFILE, F_OK) == 0)
|
||||
goto unblock;
|
||||
|
||||
// Allocate buffer
|
||||
buf = xmalloc(PATH_MAX);
|
||||
buf2 = xmalloc(PATH_MAX);
|
||||
|
||||
simple_mount("/system");
|
||||
simple_mount("/vendor");
|
||||
|
||||
unblock:
|
||||
unblock_boot_process();
|
||||
}
|
||||
|
||||
void post_fs_data(int client) {
|
||||
// Error handler
|
||||
err_handler = unblock_boot_process;
|
||||
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
if (!check_data())
|
||||
goto unblock;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Start debug logs in new process
|
||||
debug_log_fd = xopen(DEBUG_LOG, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
|
||||
char *const command[] = { "logcat", "-v", "brief", NULL };
|
||||
debug_log_pid = run_command(0, &debug_log_fd, "/system/bin/logcat", command);
|
||||
close(debug_log_fd);
|
||||
#endif
|
||||
|
||||
LOGI("** post-fs-data mode running\n");
|
||||
|
||||
// uninstaller
|
||||
if (access(UNINSTALLER, F_OK) == 0) {
|
||||
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||
system("(BOOTMODE=true sh " UNINSTALLER ") &");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate buffer
|
||||
if (buf == NULL) buf = xmalloc(PATH_MAX);
|
||||
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
|
||||
|
||||
// Cache support
|
||||
if (access("/cache/data_bin", F_OK) == 0) {
|
||||
rm_rf(DATABIN);
|
||||
rename("/cache/data_bin", DATABIN);
|
||||
}
|
||||
|
||||
// Magisk Manual Injector support
|
||||
if (access("/data/local/tmp/magisk_inject", F_OK) == 0) {
|
||||
rm_rf(DATABIN);
|
||||
rename("/data/local/tmp/magisk_inject", DATABIN);
|
||||
}
|
||||
|
||||
// Lazy.... use shell blob
|
||||
system("mv /data/magisk/stock_boot* /data;");
|
||||
|
||||
// Merge images
|
||||
if (merge_img("/cache/magisk.img", MAINIMG)) {
|
||||
LOGE("Image merge %s -> %s failed!\n", "/cache/magisk.img", MAINIMG);
|
||||
goto unblock;
|
||||
}
|
||||
if (merge_img("/data/magisk_merge.img", MAINIMG)) {
|
||||
LOGE("Image merge %s -> %s failed!\n", "/data/magisk_merge.img", MAINIMG);
|
||||
goto unblock;
|
||||
}
|
||||
|
||||
int new_img = 0;
|
||||
|
||||
if (access(MAINIMG, F_OK) == -1) {
|
||||
if (create_img(MAINIMG, 64))
|
||||
goto unblock;
|
||||
new_img = 1;
|
||||
}
|
||||
|
||||
LOGI("* Mounting " MAINIMG "\n");
|
||||
// Mounting magisk image
|
||||
char *magiskloop = mount_image(MAINIMG, MOUNTPOINT);
|
||||
if (magiskloop == NULL)
|
||||
goto unblock;
|
||||
|
||||
if (new_img) {
|
||||
xmkdir(COREDIR, 0755);
|
||||
xmkdir(COREDIR "/post-fs-data.d", 0755);
|
||||
xmkdir(COREDIR "/service.d", 0755);
|
||||
xmkdir(COREDIR "/props", 0755);
|
||||
}
|
||||
|
||||
// Run common scripts
|
||||
LOGI("* Running post-fs-data.d scripts\n");
|
||||
exec_common_script("post-fs-data");
|
||||
|
||||
// Core only mode
|
||||
if (access(DISABLEFILE, F_OK) == 0)
|
||||
goto core_only;
|
||||
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
char *module;
|
||||
struct node_entry *sys_root, *ven_root = NULL, *child;
|
||||
|
||||
dir = xopendir(MOUNTPOINT);
|
||||
|
||||
// Create the system root entry
|
||||
sys_root = xcalloc(sizeof(*sys_root), 1);
|
||||
sys_root->name = strdup("/system");
|
||||
sys_root->status = IS_INTER;
|
||||
|
||||
int has_modules = 0;
|
||||
|
||||
// Travel through each modules
|
||||
vec_init(&module_list);
|
||||
LOGI("* Loading modules\n");
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (strcmp(entry->d_name, ".") == 0 ||
|
||||
strcmp(entry->d_name, "..") == 0 ||
|
||||
strcmp(entry->d_name, ".core") == 0 ||
|
||||
strcmp(entry->d_name, "lost+found") == 0)
|
||||
continue;
|
||||
snprintf(buf, PATH_MAX, "%s/%s", MOUNTPOINT, entry->d_name);
|
||||
// Check whether remove
|
||||
snprintf(buf2, PATH_MAX, "%s/remove", buf);
|
||||
if (access(buf2, F_OK) == 0) {
|
||||
rm_rf(buf);
|
||||
continue;
|
||||
}
|
||||
// Check whether disable
|
||||
snprintf(buf2, PATH_MAX, "%s/disable", buf);
|
||||
if (access(buf2, F_OK) == 0)
|
||||
continue;
|
||||
// Add the module to list
|
||||
module = strdup(entry->d_name);
|
||||
vec_push_back(&module_list, module);
|
||||
// Read props
|
||||
snprintf(buf2, PATH_MAX, "%s/system.prop", buf);
|
||||
if (access(buf2, F_OK) == 0) {
|
||||
LOGI("%s: loading [system.prop]\n", module);
|
||||
read_prop_file(buf2, 0);
|
||||
}
|
||||
// Check whether enable auto_mount
|
||||
snprintf(buf2, PATH_MAX, "%s/auto_mount", buf);
|
||||
if (access(buf2, F_OK) == -1)
|
||||
continue;
|
||||
// Double check whether the system folder exists
|
||||
snprintf(buf2, PATH_MAX, "%s/system", buf);
|
||||
if (access(buf2, F_OK) == -1)
|
||||
continue;
|
||||
|
||||
// Construct structure
|
||||
has_modules = 1;
|
||||
LOGI("%s: constructing magic mount structure\n", module);
|
||||
// If /system/vendor exists in module, create a link outside
|
||||
snprintf(buf2, PATH_MAX, "%s/system/vendor", buf);
|
||||
if (access(buf2, F_OK) == 0) {
|
||||
snprintf(buf, PATH_MAX, "%s/%s/vendor", MOUNTPOINT, module);
|
||||
unlink(buf);
|
||||
symlink(buf2, buf);
|
||||
}
|
||||
construct_tree(module, sys_root);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
// Trim image
|
||||
umount_image(MOUNTPOINT, magiskloop);
|
||||
free(magiskloop);
|
||||
trim_img(MAINIMG);
|
||||
|
||||
// Remount them back :)
|
||||
magiskloop = mount_image(MAINIMG, MOUNTPOINT);
|
||||
free(magiskloop);
|
||||
|
||||
if (has_modules) {
|
||||
// Mount mirrors
|
||||
LOGI("* Mounting system/vendor mirrors");
|
||||
int seperate_vendor = 0;
|
||||
struct vector mounts;
|
||||
vec_init(&mounts);
|
||||
file_to_vector("/proc/mounts", &mounts);
|
||||
char *line;
|
||||
vec_for_each(&mounts, line) {
|
||||
if (strstr(line, " /system ")) {
|
||||
sscanf(line, "%s", buf);
|
||||
snprintf(buf2, PATH_MAX, "%s/system", MIRRDIR);
|
||||
xmkdir_p(buf2, 0755);
|
||||
xmount(buf, buf2, "ext4", MS_RDONLY, NULL);
|
||||
LOGI("mount: %s -> %s\n", buf, buf2);
|
||||
continue;
|
||||
}
|
||||
if (strstr(line, " /vendor ")) {
|
||||
seperate_vendor = 1;
|
||||
sscanf(line, "%s", buf);
|
||||
snprintf(buf2, PATH_MAX, "%s/vendor", MIRRDIR);
|
||||
xmkdir_p(buf2, 0755);
|
||||
xmount(buf, buf2, "ext4", MS_RDONLY, NULL);
|
||||
LOGI("mount: %s -> %s\n", buf, buf2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
vec_deep_destroy(&mounts);
|
||||
if (!seperate_vendor) {
|
||||
snprintf(buf, PATH_MAX, "%s/system/vendor", MIRRDIR);
|
||||
snprintf(buf2, PATH_MAX, "%s/vendor", MIRRDIR);
|
||||
symlink(buf, buf2);
|
||||
LOGI("link: %s -> %s\n", buf, buf2);
|
||||
}
|
||||
|
||||
// Extract the vendor node out of system tree and swap with placeholder
|
||||
vec_for_each(sys_root->children, child) {
|
||||
if (strcmp(child->name, "vendor") == 0) {
|
||||
ven_root = child;
|
||||
child = xcalloc(sizeof(*child), 1);
|
||||
child->type = seperate_vendor ? DT_LNK : DT_DIR;
|
||||
child->parent = ven_root->parent;
|
||||
child->name = ven_root->name;
|
||||
child->status = IS_VENDOR;
|
||||
vec_entry(sys_root->children)[_] = child;
|
||||
ven_root->name = strdup("/vendor");
|
||||
ven_root->parent = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Magic!!
|
||||
magic_mount(sys_root);
|
||||
if (ven_root) magic_mount(ven_root);
|
||||
}
|
||||
|
||||
// Cleanup memory
|
||||
destroy_subtree(sys_root);
|
||||
if (ven_root) destroy_subtree(ven_root);
|
||||
|
||||
// Execute module scripts
|
||||
LOGI("* Running module post-fs-data scripts\n");
|
||||
exec_module_script("post-fs-data");
|
||||
|
||||
core_only:
|
||||
// Systemless hosts
|
||||
if (access(HOSTSFILE, F_OK) == 0) {
|
||||
LOGI("* Enabling systemless hosts file support");
|
||||
bind_mount(HOSTSFILE, "/system/etc/hosts");
|
||||
}
|
||||
|
||||
// Enable magiskhide by default, only disable when set explicitly
|
||||
char *hide_prop = getprop(MAGISKHIDE_PROP);
|
||||
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, start_magisk_hide, NULL);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
free(hide_prop);
|
||||
|
||||
unblock:
|
||||
unblock_boot_process();
|
||||
}
|
||||
|
||||
void late_start(int client) {
|
||||
LOGI("** late_start service mode running\n");
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
|
||||
// Allocate buffer
|
||||
if (buf == NULL) buf = xmalloc(PATH_MAX);
|
||||
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
|
||||
|
||||
// Wait till the full patch is done
|
||||
pthread_join(sepol_patch, NULL);
|
||||
|
||||
// Run scripts after full patch, most reliable way to run scripts
|
||||
LOGI("* Running service.d scripts\n");
|
||||
exec_common_script("service");
|
||||
|
||||
// Core only mode
|
||||
if (access(DISABLEFILE, F_OK) == 0) {
|
||||
setprop("ro.magisk.disable", "1");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGI("* Running module service scripts\n");
|
||||
exec_module_script("service");
|
||||
|
||||
// Install Magisk Manager if exists
|
||||
if (access(MANAGERAPK, F_OK) == 0) {
|
||||
while (1) {
|
||||
sleep(5);
|
||||
char *const command[] = { "sh", "-c",
|
||||
"CLASSPATH=/system/framework/pm.jar "
|
||||
"/system/bin/app_process /system/bin "
|
||||
"com.android.commands.pm.Pm install -r " MANAGERAPK, NULL };
|
||||
int apk_res = -1, pid;
|
||||
pid = run_command(1, &apk_res, "/system/bin/sh", command);
|
||||
waitpid(pid, NULL, 0);
|
||||
fdgets(buf, PATH_MAX, apk_res);
|
||||
close(apk_res);
|
||||
// Keep trying until pm is started
|
||||
if (strstr(buf, "Error:") == NULL)
|
||||
break;
|
||||
}
|
||||
unlink(MANAGERAPK);
|
||||
}
|
||||
|
||||
// All boot stage done, cleanup everything
|
||||
free(buf);
|
||||
free(buf2);
|
||||
buf = buf2 = NULL;
|
||||
vec_deep_destroy(&module_list);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Stop recording the boot logcat after every boot task is done
|
||||
kill(debug_log_pid, SIGTERM);
|
||||
waitpid(debug_log_pid, NULL, 0);
|
||||
#endif
|
||||
}
|
||||
200
jni/daemon/daemon.c
Normal file
200
jni/daemon/daemon.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/* daemon.c - Magisk Daemon
|
||||
*
|
||||
* Start the daemon and wait for requests
|
||||
* Connect the daemon and send requests through sockets
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
#include "magiskpolicy.h"
|
||||
|
||||
pthread_t sepol_patch;
|
||||
|
||||
static void *request_handler(void *args) {
|
||||
// Setup the default error handler for threads
|
||||
err_handler = exit_thread;
|
||||
|
||||
int client = *((int *) args);
|
||||
free(args);
|
||||
client_request req = read_int(client);
|
||||
|
||||
struct ucred credentials;
|
||||
get_client_cred(client, &credentials);
|
||||
|
||||
switch (req) {
|
||||
case LAUNCH_MAGISKHIDE:
|
||||
case STOP_MAGISKHIDE:
|
||||
case ADD_HIDELIST:
|
||||
case RM_HIDELIST:
|
||||
case POST_FS:
|
||||
case POST_FS_DATA:
|
||||
case LATE_START:
|
||||
if (credentials.uid != 0) {
|
||||
write_int(client, ROOT_REQUIRED);
|
||||
close(client);
|
||||
return NULL;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (req) {
|
||||
case LAUNCH_MAGISKHIDE:
|
||||
launch_magiskhide(client);
|
||||
break;
|
||||
case STOP_MAGISKHIDE:
|
||||
stop_magiskhide(client);
|
||||
break;
|
||||
case ADD_HIDELIST:
|
||||
add_hide_list(client);
|
||||
break;
|
||||
case RM_HIDELIST:
|
||||
rm_hide_list(client);
|
||||
break;
|
||||
case SUPERUSER:
|
||||
su_daemon_receiver(client);
|
||||
break;
|
||||
case CHECK_VERSION:
|
||||
write_string(client, MAGISK_VER_STR);
|
||||
close(client);
|
||||
break;
|
||||
case CHECK_VERSION_CODE:
|
||||
write_int(client, MAGISK_VER_CODE);
|
||||
close(client);
|
||||
break;
|
||||
case POST_FS:
|
||||
post_fs(client);
|
||||
break;
|
||||
case POST_FS_DATA:
|
||||
post_fs_data(client);
|
||||
break;
|
||||
case LATE_START:
|
||||
late_start(client);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Setup the address and return socket fd */
|
||||
static int setup_socket(struct sockaddr_un *sun) {
|
||||
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
memset(sun, 0, sizeof(*sun));
|
||||
sun->sun_family = AF_LOCAL;
|
||||
memcpy(sun->sun_path, REQUESTOR_DAEMON_PATH, REQUESTOR_DAEMON_PATH_LEN);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void *large_sepol_patch(void *args) {
|
||||
LOGD("sepol: Starting large patch thread\n");
|
||||
// Patch su to everything
|
||||
sepol_allow("su", ALL, ALL, ALL);
|
||||
dump_policydb(SELINUX_LOAD);
|
||||
LOGD("sepol: Large patch done\n");
|
||||
destroy_policydb();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void start_daemon(int client) {
|
||||
// Launch the daemon, create new session, set proper context
|
||||
if (getuid() != UID_ROOT || getgid() != UID_ROOT) {
|
||||
fprintf(stderr, "Starting daemon requires root: %s\n", strerror(errno));
|
||||
PLOGE("start daemon");
|
||||
}
|
||||
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
PLOGE("fork");
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// First close the client, it's useless for us
|
||||
close(client);
|
||||
xsetsid();
|
||||
setcon("u:r:su:s0");
|
||||
umask(022);
|
||||
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
xdup2(fd, STDIN_FILENO);
|
||||
xdup2(fd, STDOUT_FILENO);
|
||||
xdup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
// Patch selinux with medium patch before we do anything
|
||||
load_policydb(SELINUX_POLICY);
|
||||
sepol_med_rules();
|
||||
dump_policydb(SELINUX_LOAD);
|
||||
|
||||
// Continue the larger patch in another thread, we will join later
|
||||
pthread_create(&sepol_patch, NULL, large_sepol_patch, NULL);
|
||||
|
||||
struct sockaddr_un sun;
|
||||
fd = setup_socket(&sun);
|
||||
|
||||
xbind(fd, (struct sockaddr*) &sun, sizeof(sun));
|
||||
xlisten(fd, 10);
|
||||
|
||||
// Change process name
|
||||
strcpy(argv0, "magisk_daemon");
|
||||
// The root daemon should not do anything if an error occurs
|
||||
// It should stay intact under any circumstances
|
||||
err_handler = do_nothing;
|
||||
|
||||
LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") daemon started\n");
|
||||
|
||||
// Unlock all blocks for rw
|
||||
unlock_blocks();
|
||||
|
||||
// Setup links under /sbin
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
create_links(NULL, "/sbin");
|
||||
xchmod("/sbin", 0755);
|
||||
xmkdir("/magisk", 0755);
|
||||
xchmod("/magisk", 0755);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
|
||||
// Loop forever to listen for requests
|
||||
while(1) {
|
||||
int *client = xmalloc(sizeof(int));
|
||||
*client = xaccept4(fd, NULL, NULL, SOCK_CLOEXEC);
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, request_handler, client);
|
||||
// Detach the thread, we will never join it
|
||||
pthread_detach(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect the daemon, and return a socketfd */
|
||||
int connect_daemon() {
|
||||
struct sockaddr_un sun;
|
||||
int fd = setup_socket(&sun);
|
||||
if (connect(fd, (struct sockaddr*) &sun, sizeof(sun))) {
|
||||
/* If we cannot access the daemon, we start the daemon
|
||||
* since there is no clear entry point when the daemon should be started
|
||||
*/
|
||||
LOGD("client: connect fail, try launching new daemon process\n");
|
||||
start_daemon(fd);
|
||||
do {
|
||||
// Wait for 10ms
|
||||
usleep(10);
|
||||
} while (connect(fd, (struct sockaddr*) &sun, sizeof(sun)));
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
79
jni/daemon/daemon.h
Normal file
79
jni/daemon/daemon.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* daemon.h - Utility functions for daemon-client communication
|
||||
*/
|
||||
|
||||
#ifndef _DAEMON_H_
|
||||
#define _DAEMON_H_
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
extern pthread_t sepol_patch;
|
||||
|
||||
// Commands require connecting to daemon
|
||||
typedef enum {
|
||||
DO_NOTHING = 0,
|
||||
LAUNCH_MAGISKHIDE,
|
||||
STOP_MAGISKHIDE,
|
||||
ADD_HIDELIST,
|
||||
RM_HIDELIST,
|
||||
SUPERUSER,
|
||||
CHECK_VERSION,
|
||||
CHECK_VERSION_CODE,
|
||||
POST_FS,
|
||||
POST_FS_DATA,
|
||||
LATE_START,
|
||||
TEST
|
||||
} client_request;
|
||||
|
||||
// Return codes for daemon
|
||||
typedef enum {
|
||||
DAEMON_ERROR = -1,
|
||||
DAEMON_SUCCESS = 0,
|
||||
ROOT_REQUIRED,
|
||||
HIDE_IS_ENABLED,
|
||||
HIDE_NOT_ENABLED,
|
||||
HIDE_ITEM_EXIST,
|
||||
HIDE_ITEM_NOT_EXIST,
|
||||
} daemon_response;
|
||||
|
||||
// daemon.c
|
||||
|
||||
void start_daemon(int client);
|
||||
int connect_daemon();
|
||||
|
||||
// socket_trans.c
|
||||
|
||||
int recv_fd(int sockfd);
|
||||
void send_fd(int sockfd, int fd);
|
||||
int read_int(int fd);
|
||||
void write_int(int fd, int val);
|
||||
char* read_string(int fd);
|
||||
void write_string(int fd, const char* val);
|
||||
|
||||
// log_monitor.c
|
||||
|
||||
void monitor_logs();
|
||||
|
||||
/***************
|
||||
* Boot Stages *
|
||||
***************/
|
||||
|
||||
void post_fs(int client);
|
||||
void post_fs_data(int client);
|
||||
void late_start(int client);
|
||||
|
||||
/**************
|
||||
* MagiskHide *
|
||||
**************/
|
||||
|
||||
void launch_magiskhide(int client);
|
||||
void stop_magiskhide(int client);
|
||||
void add_hide_list(int client);
|
||||
void rm_hide_list(int client);
|
||||
|
||||
/*************
|
||||
* Superuser *
|
||||
*************/
|
||||
|
||||
void su_daemon_receiver(int client);
|
||||
|
||||
#endif
|
||||
49
jni/daemon/log_monitor.c
Normal file
49
jni/daemon/log_monitor.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/* log_monitor.c - New thread to monitor logcat
|
||||
*
|
||||
* Open a new thread to call logcat and get logs with tag "Magisk"
|
||||
* Also, write the logs to a log file for debugging purpose
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
|
||||
static void *logger_thread(void *args) {
|
||||
// Setup error handler
|
||||
err_handler = exit_thread;
|
||||
|
||||
rename(LOGFILE, LASTLOG);
|
||||
int log_fd, log_pid;
|
||||
|
||||
log_fd = xopen(LOGFILE, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644);
|
||||
|
||||
while (1) {
|
||||
// Start logcat
|
||||
char *const command[] = { "logcat", "-s", "Magisk", "-v", "thread", NULL };
|
||||
log_pid = run_command(0, &log_fd, "/system/bin/logcat", command);
|
||||
if (log_pid > 0)
|
||||
waitpid(log_pid, NULL, 0);
|
||||
// For some reason it went here, clear buffer and restart
|
||||
char *const restart[] = { "logcat", "-c", NULL };
|
||||
log_pid = run_command(0, NULL, "/system/bin/logcat", restart);
|
||||
if (log_pid > 0)
|
||||
waitpid(log_pid, NULL, 0);
|
||||
}
|
||||
|
||||
// Should never be here, but well...
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Start a new thread to monitor logcat and dump to logfile */
|
||||
void monitor_logs() {
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, logger_thread, NULL);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
148
jni/daemon/socket_trans.c
Normal file
148
jni/daemon/socket_trans.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/* socket_trans.c - Functions to transfer data through socket
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
|
||||
/*
|
||||
* Receive a file descriptor from a Unix socket.
|
||||
* Contributed by @mkasick
|
||||
*
|
||||
* Returns the file descriptor on success, or -1 if a file
|
||||
* descriptor was not actually included in the message
|
||||
*
|
||||
* On error the function terminates by calling exit(-1)
|
||||
*/
|
||||
int recv_fd(int sockfd) {
|
||||
// Need to receive data from the message, otherwise don't care about it.
|
||||
char iovbuf;
|
||||
|
||||
struct iovec iov = {
|
||||
.iov_base = &iovbuf,
|
||||
.iov_len = 1,
|
||||
};
|
||||
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cmsgbuf,
|
||||
.msg_controllen = sizeof(cmsgbuf),
|
||||
};
|
||||
|
||||
xrecvmsg(sockfd, &msg, MSG_WAITALL);
|
||||
|
||||
// Was a control message actually sent?
|
||||
switch (msg.msg_controllen) {
|
||||
case 0:
|
||||
// No, so the file descriptor was closed and won't be used.
|
||||
return -1;
|
||||
case sizeof(cmsgbuf):
|
||||
// Yes, grab the file descriptor from it.
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
if (cmsg == NULL ||
|
||||
cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
|
||||
cmsg->cmsg_level != SOL_SOCKET ||
|
||||
cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
error:
|
||||
LOGE("unable to read fd");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return *(int *)CMSG_DATA(cmsg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a file descriptor through a Unix socket.
|
||||
* Contributed by @mkasick
|
||||
*
|
||||
* On error the function terminates by calling exit(-1)
|
||||
*
|
||||
* fd may be -1, in which case the dummy data is sent,
|
||||
* but no control message with the FD is sent.
|
||||
*/
|
||||
void send_fd(int sockfd, int fd) {
|
||||
// Need to send some data in the message, this will do.
|
||||
struct iovec iov = {
|
||||
.iov_base = "",
|
||||
.iov_len = 1,
|
||||
};
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
if (fd != -1) {
|
||||
// Is the file descriptor actually open?
|
||||
if (fcntl(fd, F_GETFD) == -1) {
|
||||
if (errno != EBADF) {
|
||||
PLOGE("unable to send fd");
|
||||
}
|
||||
// It's closed, don't send a control message or sendmsg will EBADF.
|
||||
} else {
|
||||
// It's open, send the file descriptor in a control message.
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
|
||||
*(int *)CMSG_DATA(cmsg) = fd;
|
||||
}
|
||||
}
|
||||
|
||||
xsendmsg(sockfd, &msg, 0);
|
||||
}
|
||||
|
||||
int read_int(int fd) {
|
||||
int val;
|
||||
xxread(fd, &val, sizeof(int));
|
||||
return val;
|
||||
}
|
||||
|
||||
void write_int(int fd, int val) {
|
||||
if (fd < 0) return;
|
||||
xwrite(fd, &val, sizeof(int));
|
||||
}
|
||||
|
||||
char* read_string(int fd) {
|
||||
int len = read_int(fd);
|
||||
if (len > PATH_MAX || len < 0) {
|
||||
LOGE("invalid string length %d", len);
|
||||
exit(1);
|
||||
}
|
||||
char* val = xmalloc(sizeof(char) * (len + 1));
|
||||
xxread(fd, val, len);
|
||||
val[len] = '\0';
|
||||
return val;
|
||||
}
|
||||
|
||||
void write_string(int fd, const char* val) {
|
||||
if (fd < 0) return;
|
||||
int len = strlen(val);
|
||||
write_int(fd, len);
|
||||
xwrite(fd, val, len);
|
||||
}
|
||||
13
jni/external/Android.mk
vendored
Normal file
13
jni/external/Android.mk
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
# libsqlite.so (stub)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libsqlite
|
||||
LOCAL_SRC_FILES := sqlite3_stub.c
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
# libselinux.so (stub)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libselinux
|
||||
LOCAL_SRC_FILES := selinux_stub.c
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
1029
jni/external/selinux/av_permissions.h
vendored
Normal file
1029
jni/external/selinux/av_permissions.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
511
jni/external/selinux/avc.h
vendored
Normal file
511
jni/external/selinux/avc.h
vendored
Normal file
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
* Access vector cache interface for object managers.
|
||||
*
|
||||
* Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
|
||||
*/
|
||||
#ifndef _SELINUX_AVC_H_
|
||||
#define _SELINUX_AVC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SID format and operations
|
||||
*/
|
||||
struct security_id {
|
||||
char * ctx;
|
||||
unsigned int refcnt;
|
||||
};
|
||||
typedef struct security_id *security_id_t;
|
||||
|
||||
#define SECSID_WILD (security_id_t)NULL /* unspecified SID */
|
||||
|
||||
/**
|
||||
* avc_sid_to_context - get copy of context corresponding to SID.
|
||||
* @sid: input SID
|
||||
* @ctx: pointer to context reference
|
||||
*
|
||||
* Return a copy of the security context corresponding to the input
|
||||
* @sid in the memory referenced by @ctx. The caller is expected to
|
||||
* free the context with freecon(). Return %0 on success, -%1 on
|
||||
* failure, with @errno set to %ENOMEM if insufficient memory was
|
||||
* available to make the copy, or %EINVAL if the input SID is invalid.
|
||||
*/
|
||||
int avc_sid_to_context(security_id_t sid, char ** ctx);
|
||||
int avc_sid_to_context_raw(security_id_t sid, char ** ctx);
|
||||
|
||||
/**
|
||||
* avc_context_to_sid - get SID for context.
|
||||
* @ctx: input security context
|
||||
* @sid: pointer to SID reference
|
||||
*
|
||||
* Look up security context @ctx in SID table, making
|
||||
* a new entry if @ctx is not found. Increment the
|
||||
* reference counter for the SID. Store a pointer
|
||||
* to the SID structure into the memory referenced by @sid,
|
||||
* returning %0 on success or -%1 on error with @errno set.
|
||||
*/
|
||||
int avc_context_to_sid(const char * ctx, security_id_t * sid);
|
||||
int avc_context_to_sid_raw(const char * ctx, security_id_t * sid);
|
||||
|
||||
/**
|
||||
* sidget - increment SID reference counter.
|
||||
* @sid: SID reference
|
||||
*
|
||||
* Increment the reference counter for @sid, indicating that
|
||||
* @sid is in use by an (additional) object. Return the
|
||||
* new reference count, or zero if @sid is invalid (has zero
|
||||
* reference count). Note that avc_context_to_sid() also
|
||||
* increments reference counts.
|
||||
*/
|
||||
int sidget(security_id_t sid);
|
||||
|
||||
/**
|
||||
* sidput - decrement SID reference counter.
|
||||
* @sid: SID reference
|
||||
*
|
||||
* Decrement the reference counter for @sid, indicating that
|
||||
* a reference to @sid is no longer in use. Return the
|
||||
* new reference count. When the reference count reaches
|
||||
* zero, the SID is invalid, and avc_context_to_sid() must
|
||||
* be called to obtain a new SID for the security context.
|
||||
*/
|
||||
int sidput(security_id_t sid);
|
||||
|
||||
/**
|
||||
* avc_get_initial_sid - get SID for an initial kernel security identifier
|
||||
* @name: input name of initial kernel security identifier
|
||||
* @sid: pointer to a SID reference
|
||||
*
|
||||
* Get the context for an initial kernel security identifier specified by
|
||||
* @name using security_get_initial_context() and then call
|
||||
* avc_context_to_sid() to get the corresponding SID.
|
||||
*/
|
||||
int avc_get_initial_sid(const char *name, security_id_t * sid);
|
||||
|
||||
/*
|
||||
* AVC entry
|
||||
*/
|
||||
struct avc_entry;
|
||||
struct avc_entry_ref {
|
||||
struct avc_entry *ae;
|
||||
};
|
||||
|
||||
/**
|
||||
* avc_entry_ref_init - initialize an AVC entry reference.
|
||||
* @aeref: pointer to avc entry reference structure
|
||||
*
|
||||
* Use this macro to initialize an avc entry reference structure
|
||||
* before first use. These structures are passed to avc_has_perm(),
|
||||
* which stores cache entry references in them. They can increase
|
||||
* performance on repeated queries.
|
||||
*/
|
||||
#define avc_entry_ref_init(aeref) ((aeref)->ae = NULL)
|
||||
|
||||
/*
|
||||
* User-provided callbacks for memory, auditing, and locking
|
||||
*/
|
||||
|
||||
/* These structures are passed by reference to avc_init(). Passing
|
||||
* a NULL reference will cause the AVC to use a default. The default
|
||||
* memory callbacks are malloc() and free(). The default logging method
|
||||
* is to print on stderr. If no thread callbacks are passed, a separate
|
||||
* listening thread won't be started for kernel policy change messages.
|
||||
* If no locking callbacks are passed, no locking will take place.
|
||||
*/
|
||||
struct avc_memory_callback {
|
||||
/* malloc() equivalent. */
|
||||
void *(*func_malloc) (size_t size);
|
||||
/* free() equivalent. */
|
||||
void (*func_free) (void *ptr);
|
||||
/* Note that these functions should set errno on failure.
|
||||
If not, some avc routines may return -1 without errno set. */
|
||||
};
|
||||
|
||||
struct avc_log_callback {
|
||||
/* log the printf-style format and arguments. */
|
||||
void
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((format(printf, 1, 2)))
|
||||
#endif
|
||||
(*func_log) (const char *fmt, ...);
|
||||
/* store a string representation of auditdata (corresponding
|
||||
to the given security class) into msgbuf. */
|
||||
void (*func_audit) (void *auditdata, security_class_t cls,
|
||||
char *msgbuf, size_t msgbufsize);
|
||||
};
|
||||
|
||||
struct avc_thread_callback {
|
||||
/* create and start a thread, returning an opaque pointer to it;
|
||||
the thread should run the given function. */
|
||||
void *(*func_create_thread) (void (*run) (void));
|
||||
/* cancel a given thread and free its resources. */
|
||||
void (*func_stop_thread) (void *thread);
|
||||
};
|
||||
|
||||
struct avc_lock_callback {
|
||||
/* create a lock and return an opaque pointer to it. */
|
||||
void *(*func_alloc_lock) (void);
|
||||
/* obtain a given lock, blocking if necessary. */
|
||||
void (*func_get_lock) (void *lock);
|
||||
/* release a given lock. */
|
||||
void (*func_release_lock) (void *lock);
|
||||
/* destroy a given lock (free memory, etc.) */
|
||||
void (*func_free_lock) (void *lock);
|
||||
};
|
||||
|
||||
/*
|
||||
* Available options
|
||||
*/
|
||||
|
||||
/* no-op option, useful for unused slots in an array of options */
|
||||
#define AVC_OPT_UNUSED 0
|
||||
/* override kernel enforcing mode (boolean value) */
|
||||
#define AVC_OPT_SETENFORCE 1
|
||||
|
||||
/*
|
||||
* AVC operations
|
||||
*/
|
||||
|
||||
/**
|
||||
* avc_init - Initialize the AVC.
|
||||
* @msgprefix: prefix for log messages
|
||||
* @mem_callbacks: user-supplied memory callbacks
|
||||
* @log_callbacks: user-supplied logging callbacks
|
||||
* @thread_callbacks: user-supplied threading callbacks
|
||||
* @lock_callbacks: user-supplied locking callbacks
|
||||
*
|
||||
* Initialize the access vector cache. Return %0 on
|
||||
* success or -%1 with @errno set on failure.
|
||||
* If @msgprefix is NULL, use "uavc". If any callback
|
||||
* structure references are NULL, use default methods
|
||||
* for those callbacks (see the definition of the callback
|
||||
* structures above).
|
||||
*/
|
||||
int avc_init(const char *msgprefix,
|
||||
const struct avc_memory_callback *mem_callbacks,
|
||||
const struct avc_log_callback *log_callbacks,
|
||||
const struct avc_thread_callback *thread_callbacks,
|
||||
const struct avc_lock_callback *lock_callbacks);
|
||||
|
||||
/**
|
||||
* avc_open - Initialize the AVC.
|
||||
* @opts: array of selabel_opt structures specifying AVC options or NULL.
|
||||
* @nopts: number of elements in opts array or zero for no options.
|
||||
*
|
||||
* This function is identical to avc_init(), except the message prefix
|
||||
* is set to "avc" and any callbacks desired should be specified via
|
||||
* selinux_set_callback(). Available options are listed above.
|
||||
*/
|
||||
int avc_open(struct selinux_opt *opts, unsigned nopts);
|
||||
|
||||
/**
|
||||
* avc_cleanup - Remove unused SIDs and AVC entries.
|
||||
*
|
||||
* Search the SID table for SID structures with zero
|
||||
* reference counts, and remove them along with all
|
||||
* AVC entries that reference them. This can be used
|
||||
* to return memory to the system.
|
||||
*/
|
||||
void avc_cleanup(void);
|
||||
|
||||
/**
|
||||
* avc_reset - Flush the cache and reset statistics.
|
||||
*
|
||||
* Remove all entries from the cache and reset all access
|
||||
* statistics (as returned by avc_cache_stats()) to zero.
|
||||
* The SID mapping is not affected. Return %0 on success,
|
||||
* -%1 with @errno set on error.
|
||||
*/
|
||||
int avc_reset(void);
|
||||
|
||||
/**
|
||||
* avc_destroy - Free all AVC structures.
|
||||
*
|
||||
* Destroy all AVC structures and free all allocated
|
||||
* memory. User-supplied locking, memory, and audit
|
||||
* callbacks will be retained, but security-event
|
||||
* callbacks will not. All SID's will be invalidated.
|
||||
* User must call avc_init() if further use of AVC is desired.
|
||||
*/
|
||||
void avc_destroy(void);
|
||||
|
||||
/**
|
||||
* avc_has_perm_noaudit - Check permissions but perform no auditing.
|
||||
* @ssid: source security identifier
|
||||
* @tsid: target security identifier
|
||||
* @tclass: target security class
|
||||
* @requested: requested permissions, interpreted based on @tclass
|
||||
* @aeref: AVC entry reference
|
||||
* @avd: access vector decisions
|
||||
*
|
||||
* Check the AVC to determine whether the @requested permissions are granted
|
||||
* for the SID pair (@ssid, @tsid), interpreting the permissions
|
||||
* based on @tclass, and call the security server on a cache miss to obtain
|
||||
* a new decision and add it to the cache. Update @aeref to refer to an AVC
|
||||
* entry with the resulting decisions, and return a copy of the decisions
|
||||
* in @avd. Return %0 if all @requested permissions are granted, -%1 with
|
||||
* @errno set to %EACCES if any permissions are denied, or to another value
|
||||
* upon other errors. This function is typically called by avc_has_perm(),
|
||||
* but may also be called directly to separate permission checking from
|
||||
* auditing, e.g. in cases where a lock must be held for the check but
|
||||
* should be released for the auditing.
|
||||
*/
|
||||
int avc_has_perm_noaudit(security_id_t ssid,
|
||||
security_id_t tsid,
|
||||
security_class_t tclass,
|
||||
access_vector_t requested,
|
||||
struct avc_entry_ref *aeref, struct av_decision *avd);
|
||||
|
||||
/**
|
||||
* avc_has_perm - Check permissions and perform any appropriate auditing.
|
||||
* @ssid: source security identifier
|
||||
* @tsid: target security identifier
|
||||
* @tclass: target security class
|
||||
* @requested: requested permissions, interpreted based on @tclass
|
||||
* @aeref: AVC entry reference
|
||||
* @auditdata: auxiliary audit data
|
||||
*
|
||||
* Check the AVC to determine whether the @requested permissions are granted
|
||||
* for the SID pair (@ssid, @tsid), interpreting the permissions
|
||||
* based on @tclass, and call the security server on a cache miss to obtain
|
||||
* a new decision and add it to the cache. Update @aeref to refer to an AVC
|
||||
* entry with the resulting decisions. Audit the granting or denial of
|
||||
* permissions in accordance with the policy. Return %0 if all @requested
|
||||
* permissions are granted, -%1 with @errno set to %EACCES if any permissions
|
||||
* are denied or to another value upon other errors.
|
||||
*/
|
||||
int avc_has_perm(security_id_t ssid, security_id_t tsid,
|
||||
security_class_t tclass, access_vector_t requested,
|
||||
struct avc_entry_ref *aeref, void *auditdata);
|
||||
|
||||
/**
|
||||
* avc_audit - Audit the granting or denial of permissions.
|
||||
* @ssid: source security identifier
|
||||
* @tsid: target security identifier
|
||||
* @tclass: target security class
|
||||
* @requested: requested permissions
|
||||
* @avd: access vector decisions
|
||||
* @result: result from avc_has_perm_noaudit
|
||||
* @auditdata: auxiliary audit data
|
||||
*
|
||||
* Audit the granting or denial of permissions in accordance
|
||||
* with the policy. This function is typically called by
|
||||
* avc_has_perm() after a permission check, but can also be
|
||||
* called directly by callers who use avc_has_perm_noaudit()
|
||||
* in order to separate the permission check from the auditing.
|
||||
* For example, this separation is useful when the permission check must
|
||||
* be performed under a lock, to allow the lock to be released
|
||||
* before calling the auditing code.
|
||||
*/
|
||||
void avc_audit(security_id_t ssid, security_id_t tsid,
|
||||
security_class_t tclass, access_vector_t requested,
|
||||
struct av_decision *avd, int result, void *auditdata);
|
||||
|
||||
/**
|
||||
* avc_compute_create - Compute SID for labeling a new object.
|
||||
* @ssid: source security identifier
|
||||
* @tsid: target security identifier
|
||||
* @tclass: target security class
|
||||
* @newsid: pointer to SID reference
|
||||
*
|
||||
* Call the security server to obtain a context for labeling a
|
||||
* new object. Look up the context in the SID table, making
|
||||
* a new entry if not found. Increment the reference counter
|
||||
* for the SID. Store a pointer to the SID structure into the
|
||||
* memory referenced by @newsid, returning %0 on success or -%1 on
|
||||
* error with @errno set.
|
||||
*/
|
||||
int avc_compute_create(security_id_t ssid,
|
||||
security_id_t tsid,
|
||||
security_class_t tclass, security_id_t * newsid);
|
||||
|
||||
/**
|
||||
* avc_compute_member - Compute SID for polyinstantation.
|
||||
* @ssid: source security identifier
|
||||
* @tsid: target security identifier
|
||||
* @tclass: target security class
|
||||
* @newsid: pointer to SID reference
|
||||
*
|
||||
* Call the security server to obtain a context for labeling an
|
||||
* object instance. Look up the context in the SID table, making
|
||||
* a new entry if not found. Increment the reference counter
|
||||
* for the SID. Store a pointer to the SID structure into the
|
||||
* memory referenced by @newsid, returning %0 on success or -%1 on
|
||||
* error with @errno set.
|
||||
*/
|
||||
int avc_compute_member(security_id_t ssid,
|
||||
security_id_t tsid,
|
||||
security_class_t tclass, security_id_t * newsid);
|
||||
|
||||
/*
|
||||
* security event callback facility
|
||||
*/
|
||||
|
||||
/* security events */
|
||||
#define AVC_CALLBACK_GRANT 1
|
||||
#define AVC_CALLBACK_TRY_REVOKE 2
|
||||
#define AVC_CALLBACK_REVOKE 4
|
||||
#define AVC_CALLBACK_RESET 8
|
||||
#define AVC_CALLBACK_AUDITALLOW_ENABLE 16
|
||||
#define AVC_CALLBACK_AUDITALLOW_DISABLE 32
|
||||
#define AVC_CALLBACK_AUDITDENY_ENABLE 64
|
||||
#define AVC_CALLBACK_AUDITDENY_DISABLE 128
|
||||
|
||||
/**
|
||||
* avc_add_callback - Register a callback for security events.
|
||||
* @callback: callback function
|
||||
* @events: bitwise OR of desired security events
|
||||
* @ssid: source security identifier or %SECSID_WILD
|
||||
* @tsid: target security identifier or %SECSID_WILD
|
||||
* @tclass: target security class
|
||||
* @perms: permissions
|
||||
*
|
||||
* Register a callback function for events in the set @events
|
||||
* related to the SID pair (@ssid, @tsid) and
|
||||
* and the permissions @perms, interpreting
|
||||
* @perms based on @tclass. Returns %0 on success or
|
||||
* -%1 if insufficient memory exists to add the callback.
|
||||
*/
|
||||
int avc_add_callback(int (*callback)
|
||||
(uint32_t event, security_id_t ssid,
|
||||
security_id_t tsid, security_class_t tclass,
|
||||
access_vector_t perms,
|
||||
access_vector_t * out_retained),
|
||||
uint32_t events, security_id_t ssid,
|
||||
security_id_t tsid, security_class_t tclass,
|
||||
access_vector_t perms);
|
||||
|
||||
/*
|
||||
* AVC statistics
|
||||
*/
|
||||
|
||||
/* If set, cache statistics are tracked. This may
|
||||
* become a compile-time option in the future.
|
||||
*/
|
||||
#define AVC_CACHE_STATS 1
|
||||
|
||||
struct avc_cache_stats {
|
||||
unsigned entry_lookups;
|
||||
unsigned entry_hits;
|
||||
unsigned entry_misses;
|
||||
unsigned entry_discards;
|
||||
unsigned cav_lookups;
|
||||
unsigned cav_hits;
|
||||
unsigned cav_probes;
|
||||
unsigned cav_misses;
|
||||
};
|
||||
|
||||
/**
|
||||
* avc_cache_stats - get cache access statistics.
|
||||
* @stats: reference to statistics structure
|
||||
*
|
||||
* Fill the supplied structure with information about AVC
|
||||
* activity since the last call to avc_init() or
|
||||
* avc_reset(). See the structure definition for
|
||||
* details.
|
||||
*/
|
||||
void avc_cache_stats(struct avc_cache_stats *stats);
|
||||
|
||||
/**
|
||||
* avc_av_stats - log av table statistics.
|
||||
*
|
||||
* Log a message with information about the size and
|
||||
* distribution of the access vector table. The audit
|
||||
* callback is used to print the message.
|
||||
*/
|
||||
void avc_av_stats(void);
|
||||
|
||||
/**
|
||||
* avc_sid_stats - log SID table statistics.
|
||||
*
|
||||
* Log a message with information about the size and
|
||||
* distribution of the SID table. The audit callback
|
||||
* is used to print the message.
|
||||
*/
|
||||
void avc_sid_stats(void);
|
||||
|
||||
/**
|
||||
* avc_netlink_open - Create a netlink socket and connect to the kernel.
|
||||
*/
|
||||
int avc_netlink_open(int blocking);
|
||||
|
||||
/**
|
||||
* avc_netlink_loop - Wait for netlink messages from the kernel
|
||||
*/
|
||||
void avc_netlink_loop(void);
|
||||
|
||||
/**
|
||||
* avc_netlink_close - Close the netlink socket
|
||||
*/
|
||||
void avc_netlink_close(void);
|
||||
|
||||
/**
|
||||
* avc_netlink_acquire_fd - Acquire netlink socket fd.
|
||||
*
|
||||
* Allows the application to manage messages from the netlink socket in
|
||||
* its own main loop.
|
||||
*/
|
||||
int avc_netlink_acquire_fd(void);
|
||||
|
||||
/**
|
||||
* avc_netlink_release_fd - Release netlink socket fd.
|
||||
*
|
||||
* Returns ownership of the netlink socket to the library.
|
||||
*/
|
||||
void avc_netlink_release_fd(void);
|
||||
|
||||
/**
|
||||
* avc_netlink_check_nb - Check netlink socket for new messages.
|
||||
*
|
||||
* Called by the application when using avc_netlink_acquire_fd() to
|
||||
* process kernel netlink events.
|
||||
*/
|
||||
int avc_netlink_check_nb(void);
|
||||
|
||||
/**
|
||||
* selinux_status_open - Open and map SELinux kernel status page
|
||||
*
|
||||
*/
|
||||
int selinux_status_open(int fallback);
|
||||
|
||||
/**
|
||||
* selinux_status_close - Unmap and close SELinux kernel status page
|
||||
*
|
||||
*/
|
||||
void selinux_status_close(void);
|
||||
|
||||
/**
|
||||
* selinux_status_updated - Inform us whether the kernel status has been updated
|
||||
*
|
||||
*/
|
||||
int selinux_status_updated(void);
|
||||
|
||||
/**
|
||||
* selinux_status_getenforce - Get the enforce flag value
|
||||
*
|
||||
*/
|
||||
int selinux_status_getenforce(void);
|
||||
|
||||
/**
|
||||
* selinux_status_policyload - Get the number of policy reloaded
|
||||
*
|
||||
*/
|
||||
int selinux_status_policyload(void);
|
||||
|
||||
/**
|
||||
* selinux_status_deny_unknown - Get the behavior for undefined classes/permissions
|
||||
*
|
||||
*/
|
||||
int selinux_status_deny_unknown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _SELINUX_AVC_H_ */
|
||||
50
jni/external/selinux/context.h
vendored
Normal file
50
jni/external/selinux/context.h
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef _SELINUX_CONTEXT_H_
|
||||
#define _SELINUX_CONTEXT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Functions to deal with security contexts in user space.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
void *ptr;
|
||||
} context_s_t;
|
||||
|
||||
typedef context_s_t *context_t;
|
||||
|
||||
/* Return a new context initialized to a context string */
|
||||
|
||||
extern context_t context_new(const char *);
|
||||
|
||||
/*
|
||||
* Return a pointer to the string value of the context_t
|
||||
* Valid until the next call to context_str or context_free
|
||||
* for the same context_t*
|
||||
*/
|
||||
|
||||
extern char *context_str(context_t);
|
||||
|
||||
/* Free the storage used by a context */
|
||||
extern void context_free(context_t);
|
||||
|
||||
/* Get a pointer to the string value of a context component */
|
||||
|
||||
extern const char *context_type_get(context_t);
|
||||
extern const char *context_range_get(context_t);
|
||||
extern const char *context_role_get(context_t);
|
||||
extern const char *context_user_get(context_t);
|
||||
|
||||
/* Set a context component. Returns nonzero if unsuccessful */
|
||||
|
||||
extern int context_type_set(context_t, const char *);
|
||||
extern int context_range_set(context_t, const char *);
|
||||
extern int context_role_set(context_t, const char *);
|
||||
extern int context_user_set(context_t, const char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
118
jni/external/selinux/flask.h
vendored
Normal file
118
jni/external/selinux/flask.h
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/* This file is automatically generated. Do not edit. */
|
||||
#ifndef _SELINUX_FLASK_H_
|
||||
#define _SELINUX_FLASK_H_
|
||||
|
||||
#warning "Please remove any #include's of this header in your source code."
|
||||
#warning "Instead, use string_to_security_class() to map the class name to a value."
|
||||
|
||||
/*
|
||||
* Security object class definitions
|
||||
*/
|
||||
#define SECCLASS_SECURITY 1
|
||||
#define SECCLASS_PROCESS 2
|
||||
#define SECCLASS_SYSTEM 3
|
||||
#define SECCLASS_CAPABILITY 4
|
||||
#define SECCLASS_FILESYSTEM 5
|
||||
#define SECCLASS_FILE 6
|
||||
#define SECCLASS_DIR 7
|
||||
#define SECCLASS_FD 8
|
||||
#define SECCLASS_LNK_FILE 9
|
||||
#define SECCLASS_CHR_FILE 10
|
||||
#define SECCLASS_BLK_FILE 11
|
||||
#define SECCLASS_SOCK_FILE 12
|
||||
#define SECCLASS_FIFO_FILE 13
|
||||
#define SECCLASS_SOCKET 14
|
||||
#define SECCLASS_TCP_SOCKET 15
|
||||
#define SECCLASS_UDP_SOCKET 16
|
||||
#define SECCLASS_RAWIP_SOCKET 17
|
||||
#define SECCLASS_NODE 18
|
||||
#define SECCLASS_NETIF 19
|
||||
#define SECCLASS_NETLINK_SOCKET 20
|
||||
#define SECCLASS_PACKET_SOCKET 21
|
||||
#define SECCLASS_KEY_SOCKET 22
|
||||
#define SECCLASS_UNIX_STREAM_SOCKET 23
|
||||
#define SECCLASS_UNIX_DGRAM_SOCKET 24
|
||||
#define SECCLASS_SEM 25
|
||||
#define SECCLASS_MSG 26
|
||||
#define SECCLASS_MSGQ 27
|
||||
#define SECCLASS_SHM 28
|
||||
#define SECCLASS_IPC 29
|
||||
#define SECCLASS_PASSWD 30
|
||||
#define SECCLASS_X_DRAWABLE 31
|
||||
#define SECCLASS_X_SCREEN 32
|
||||
#define SECCLASS_X_GC 33
|
||||
#define SECCLASS_X_FONT 34
|
||||
#define SECCLASS_X_COLORMAP 35
|
||||
#define SECCLASS_X_PROPERTY 36
|
||||
#define SECCLASS_X_SELECTION 37
|
||||
#define SECCLASS_X_CURSOR 38
|
||||
#define SECCLASS_X_CLIENT 39
|
||||
#define SECCLASS_X_DEVICE 40
|
||||
#define SECCLASS_X_SERVER 41
|
||||
#define SECCLASS_X_EXTENSION 42
|
||||
#define SECCLASS_NETLINK_ROUTE_SOCKET 43
|
||||
#define SECCLASS_NETLINK_FIREWALL_SOCKET 44
|
||||
#define SECCLASS_NETLINK_TCPDIAG_SOCKET 45
|
||||
#define SECCLASS_NETLINK_NFLOG_SOCKET 46
|
||||
#define SECCLASS_NETLINK_XFRM_SOCKET 47
|
||||
#define SECCLASS_NETLINK_SELINUX_SOCKET 48
|
||||
#define SECCLASS_NETLINK_AUDIT_SOCKET 49
|
||||
#define SECCLASS_NETLINK_IP6FW_SOCKET 50
|
||||
#define SECCLASS_NETLINK_DNRT_SOCKET 51
|
||||
#define SECCLASS_DBUS 52
|
||||
#define SECCLASS_NSCD 53
|
||||
#define SECCLASS_ASSOCIATION 54
|
||||
#define SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET 55
|
||||
#define SECCLASS_APPLETALK_SOCKET 56
|
||||
#define SECCLASS_PACKET 57
|
||||
#define SECCLASS_KEY 58
|
||||
#define SECCLASS_CONTEXT 59
|
||||
#define SECCLASS_DCCP_SOCKET 60
|
||||
#define SECCLASS_MEMPROTECT 61
|
||||
#define SECCLASS_DB_DATABASE 62
|
||||
#define SECCLASS_DB_TABLE 63
|
||||
#define SECCLASS_DB_PROCEDURE 64
|
||||
#define SECCLASS_DB_COLUMN 65
|
||||
#define SECCLASS_DB_TUPLE 66
|
||||
#define SECCLASS_DB_BLOB 67
|
||||
#define SECCLASS_PEER 68
|
||||
#define SECCLASS_CAPABILITY2 69
|
||||
#define SECCLASS_X_RESOURCE 70
|
||||
#define SECCLASS_X_EVENT 71
|
||||
#define SECCLASS_X_SYNTHETIC_EVENT 72
|
||||
#define SECCLASS_X_APPLICATION_DATA 73
|
||||
|
||||
/*
|
||||
* Security identifier indices for initial entities
|
||||
*/
|
||||
#define SECINITSID_KERNEL 1
|
||||
#define SECINITSID_SECURITY 2
|
||||
#define SECINITSID_UNLABELED 3
|
||||
#define SECINITSID_FS 4
|
||||
#define SECINITSID_FILE 5
|
||||
#define SECINITSID_FILE_LABELS 6
|
||||
#define SECINITSID_INIT 7
|
||||
#define SECINITSID_ANY_SOCKET 8
|
||||
#define SECINITSID_PORT 9
|
||||
#define SECINITSID_NETIF 10
|
||||
#define SECINITSID_NETMSG 11
|
||||
#define SECINITSID_NODE 12
|
||||
#define SECINITSID_IGMP_PACKET 13
|
||||
#define SECINITSID_ICMP_SOCKET 14
|
||||
#define SECINITSID_TCP_SOCKET 15
|
||||
#define SECINITSID_SYSCTL_MODPROBE 16
|
||||
#define SECINITSID_SYSCTL 17
|
||||
#define SECINITSID_SYSCTL_FS 18
|
||||
#define SECINITSID_SYSCTL_KERNEL 19
|
||||
#define SECINITSID_SYSCTL_NET 20
|
||||
#define SECINITSID_SYSCTL_NET_UNIX 21
|
||||
#define SECINITSID_SYSCTL_VM 22
|
||||
#define SECINITSID_SYSCTL_DEV 23
|
||||
#define SECINITSID_KMOD 24
|
||||
#define SECINITSID_POLICY 25
|
||||
#define SECINITSID_SCMP_PACKET 26
|
||||
#define SECINITSID_DEVNULL 27
|
||||
|
||||
#define SECINITSID_NUM 27
|
||||
|
||||
#endif
|
||||
82
jni/external/selinux/get_context_list.h
vendored
Normal file
82
jni/external/selinux/get_context_list.h
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
#ifndef _SELINUX_GET_SID_LIST_H_
|
||||
#define _SELINUX_GET_SID_LIST_H_
|
||||
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SELINUX_DEFAULTUSER "user_u"
|
||||
|
||||
/* Get an ordered list of authorized security contexts for a user session
|
||||
for 'user' spawned by 'fromcon' and set *conary to refer to the
|
||||
NULL-terminated array of contexts. Every entry in the list will
|
||||
be authorized by the policy, but the ordering is subject to user
|
||||
customizable preferences. Returns number of entries in *conary.
|
||||
If 'fromcon' is NULL, defaults to current context.
|
||||
Caller must free via freeconary. */
|
||||
extern int get_ordered_context_list(const char *user,
|
||||
char * fromcon,
|
||||
char *** list);
|
||||
|
||||
/* As above, but use the provided MLS level rather than the
|
||||
default level for the user. */
|
||||
int get_ordered_context_list_with_level(const char *user,
|
||||
const char *level,
|
||||
char * fromcon,
|
||||
char *** list);
|
||||
|
||||
/* Get the default security context for a user session for 'user'
|
||||
spawned by 'fromcon' and set *newcon to refer to it. The context
|
||||
will be one of those authorized by the policy, but the selection
|
||||
of a default is subject to user customizable preferences.
|
||||
If 'fromcon' is NULL, defaults to current context.
|
||||
Returns 0 on success or -1 otherwise.
|
||||
Caller must free via freecon. */
|
||||
extern int get_default_context(const char *user,
|
||||
char * fromcon,
|
||||
char ** newcon);
|
||||
|
||||
/* As above, but use the provided MLS level rather than the
|
||||
default level for the user. */
|
||||
int get_default_context_with_level(const char *user,
|
||||
const char *level,
|
||||
char * fromcon,
|
||||
char ** newcon);
|
||||
|
||||
/* Same as get_default_context, but only return a context
|
||||
that has the specified role. If no reachable context exists
|
||||
for the user with that role, then return -1. */
|
||||
int get_default_context_with_role(const char *user,
|
||||
const char *role,
|
||||
char * fromcon,
|
||||
char ** newcon);
|
||||
|
||||
/* Same as get_default_context, but only return a context
|
||||
that has the specified role and level. If no reachable context exists
|
||||
for the user with that role, then return -1. */
|
||||
int get_default_context_with_rolelevel(const char *user,
|
||||
const char *role,
|
||||
const char *level,
|
||||
char * fromcon,
|
||||
char ** newcon);
|
||||
|
||||
/* Given a list of authorized security contexts for the user,
|
||||
query the user to select one and set *newcon to refer to it.
|
||||
Caller must free via freecon.
|
||||
Returns 0 on sucess or -1 otherwise. */
|
||||
extern int query_user_context(char ** list,
|
||||
char ** newcon);
|
||||
|
||||
/* Allow the user to manually enter a context as a fallback
|
||||
if a list of authorized contexts could not be obtained.
|
||||
Caller must free via freecon.
|
||||
Returns 0 on success or -1 otherwise. */
|
||||
extern int manual_user_enter_context(const char *user,
|
||||
char ** newcon);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
23
jni/external/selinux/get_default_type.h
vendored
Normal file
23
jni/external/selinux/get_default_type.h
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/* get_default_type.h - contains header information and function prototypes
|
||||
* for functions to get the default type for a role
|
||||
*/
|
||||
|
||||
#ifndef _SELINUX_GET_DEFAULT_TYPE_H_
|
||||
#define _SELINUX_GET_DEFAULT_TYPE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Return path to default type file. */
|
||||
const char *selinux_default_type_path(void);
|
||||
|
||||
/* Get the default type (domain) for 'role' and set 'type' to refer to it.
|
||||
Caller must free via free().
|
||||
Return 0 on success or -1 otherwise. */
|
||||
int get_default_type(const char *role, char **type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* ifndef _GET_DEFAULT_TYPE_H_ */
|
||||
190
jni/external/selinux/label.h
vendored
Normal file
190
jni/external/selinux/label.h
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Labeling interface for userspace object managers and others.
|
||||
*
|
||||
* Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
|
||||
*/
|
||||
#ifndef _SELABEL_H_
|
||||
#define _SELABEL_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Opaque type used for all label handles.
|
||||
*/
|
||||
|
||||
struct selabel_handle;
|
||||
|
||||
/*
|
||||
* Available backends.
|
||||
*/
|
||||
|
||||
/* file contexts */
|
||||
#define SELABEL_CTX_FILE 0
|
||||
/* media contexts */
|
||||
#define SELABEL_CTX_MEDIA 1
|
||||
/* x contexts */
|
||||
#define SELABEL_CTX_X 2
|
||||
/* db objects */
|
||||
#define SELABEL_CTX_DB 3
|
||||
/* Android property service contexts */
|
||||
#define SELABEL_CTX_ANDROID_PROP 4
|
||||
/* Android service contexts */
|
||||
#define SELABEL_CTX_ANDROID_SERVICE 5
|
||||
|
||||
/*
|
||||
* Available options
|
||||
*/
|
||||
|
||||
/* no-op option, useful for unused slots in an array of options */
|
||||
#define SELABEL_OPT_UNUSED 0
|
||||
/* validate contexts before returning them (boolean value) */
|
||||
#define SELABEL_OPT_VALIDATE 1
|
||||
/* don't use local customizations to backend data (boolean value) */
|
||||
#define SELABEL_OPT_BASEONLY 2
|
||||
/* specify an alternate path to use when loading backend data */
|
||||
#define SELABEL_OPT_PATH 3
|
||||
/* select a subset of the search space as an optimization (file backend) */
|
||||
#define SELABEL_OPT_SUBSET 4
|
||||
/* require a hash calculation on spec files */
|
||||
#define SELABEL_OPT_DIGEST 5
|
||||
/* total number of options */
|
||||
#define SELABEL_NOPT 6
|
||||
|
||||
/*
|
||||
* Label operations
|
||||
*/
|
||||
|
||||
/**
|
||||
* selabel_open - Create a labeling handle.
|
||||
* @backend: one of the constants specifying a supported labeling backend.
|
||||
* @opts: array of selabel_opt structures specifying label options or NULL.
|
||||
* @nopts: number of elements in opts array or zero for no options.
|
||||
*
|
||||
* Open a labeling backend for use. The available backend identifiers are
|
||||
* listed above. Options may be provided via the opts parameter; available
|
||||
* options are listed above. Not all options may be supported by every
|
||||
* backend. Return value is the created handle on success or NULL with
|
||||
* @errno set on failure.
|
||||
*/
|
||||
struct selabel_handle *selabel_open(unsigned int backend,
|
||||
const struct selinux_opt *opts,
|
||||
unsigned nopts);
|
||||
|
||||
/**
|
||||
* selabel_close - Close a labeling handle.
|
||||
* @handle: specifies handle to close
|
||||
*
|
||||
* Destroy the specified handle, closing files, freeing allocated memory,
|
||||
* etc. The handle may not be further used after it has been closed.
|
||||
*/
|
||||
void selabel_close(struct selabel_handle *handle);
|
||||
|
||||
/**
|
||||
* selabel_lookup - Perform labeling lookup operation.
|
||||
* @handle: specifies backend instance to query
|
||||
* @con: returns the appropriate context with which to label the object
|
||||
* @key: string input to lookup operation
|
||||
* @type: numeric input to the lookup operation
|
||||
*
|
||||
* Perform a labeling lookup operation. Return %0 on success, -%1 with
|
||||
* @errno set on failure. The key and type arguments are the inputs to the
|
||||
* lookup operation; appropriate values are dictated by the backend in use.
|
||||
* The result is returned in the memory pointed to by @con and must be freed
|
||||
* by the user with freecon().
|
||||
*/
|
||||
int selabel_lookup(struct selabel_handle *handle, char **con,
|
||||
const char *key, int type);
|
||||
int selabel_lookup_raw(struct selabel_handle *handle, char **con,
|
||||
const char *key, int type);
|
||||
|
||||
bool selabel_partial_match(struct selabel_handle *handle, const char *key);
|
||||
|
||||
int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
|
||||
const char *key, const char **aliases, int type);
|
||||
int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con,
|
||||
const char *key, const char **aliases, int type);
|
||||
|
||||
/**
|
||||
* selabel_digest - Retrieve the SHA1 digest and the list of specfiles used to
|
||||
* generate the digest. The SELABEL_OPT_DIGEST option must
|
||||
* be set in selabel_open() to initiate the digest generation.
|
||||
* @handle: specifies backend instance to query
|
||||
* @digest: returns a pointer to the SHA1 digest.
|
||||
* @digest_len: returns length of digest in bytes.
|
||||
* @specfiles: a list of specfiles used in the SHA1 digest generation.
|
||||
* The list is NULL terminated and will hold @num_specfiles entries.
|
||||
* @num_specfiles: number of specfiles in the list.
|
||||
*
|
||||
* Return %0 on success, -%1 with @errno set on failure.
|
||||
*/
|
||||
int selabel_digest(struct selabel_handle *rec,
|
||||
unsigned char **digest, size_t *digest_len,
|
||||
char ***specfiles, size_t *num_specfiles);
|
||||
|
||||
enum selabel_cmp_result {
|
||||
SELABEL_SUBSET,
|
||||
SELABEL_EQUAL,
|
||||
SELABEL_SUPERSET,
|
||||
SELABEL_INCOMPARABLE
|
||||
};
|
||||
|
||||
/**
|
||||
* selabel_cmp - Compare two label configurations.
|
||||
* @h1: handle for the first label configuration
|
||||
* @h2: handle for the first label configuration
|
||||
*
|
||||
* Compare two label configurations.
|
||||
* Return %SELABEL_SUBSET if @h1 is a subset of @h2, %SELABEL_EQUAL
|
||||
* if @h1 is identical to @h2, %SELABEL_SUPERSET if @h1 is a superset
|
||||
* of @h2, and %SELABEL_INCOMPARABLE if @h1 and @h2 are incomparable.
|
||||
*/
|
||||
enum selabel_cmp_result selabel_cmp(struct selabel_handle *h1,
|
||||
struct selabel_handle *h2);
|
||||
|
||||
/**
|
||||
* selabel_stats - log labeling operation statistics.
|
||||
* @handle: specifies backend instance to query
|
||||
*
|
||||
* Log a message with information about the number of queries performed,
|
||||
* number of unused matching entries, or other operational statistics.
|
||||
* Message is backend-specific, some backends may not output a message.
|
||||
*/
|
||||
void selabel_stats(struct selabel_handle *handle);
|
||||
|
||||
/*
|
||||
* Type codes used by specific backends
|
||||
*/
|
||||
|
||||
/* X backend */
|
||||
#define SELABEL_X_PROP 1
|
||||
#define SELABEL_X_EXT 2
|
||||
#define SELABEL_X_CLIENT 3
|
||||
#define SELABEL_X_EVENT 4
|
||||
#define SELABEL_X_SELN 5
|
||||
#define SELABEL_X_POLYPROP 6
|
||||
#define SELABEL_X_POLYSELN 7
|
||||
|
||||
/* DB backend */
|
||||
#define SELABEL_DB_DATABASE 1
|
||||
#define SELABEL_DB_SCHEMA 2
|
||||
#define SELABEL_DB_TABLE 3
|
||||
#define SELABEL_DB_COLUMN 4
|
||||
#define SELABEL_DB_SEQUENCE 5
|
||||
#define SELABEL_DB_VIEW 6
|
||||
#define SELABEL_DB_PROCEDURE 7
|
||||
#define SELABEL_DB_BLOB 8
|
||||
#define SELABEL_DB_TUPLE 9
|
||||
#define SELABEL_DB_LANGUAGE 10
|
||||
#define SELABEL_DB_EXCEPTION 11
|
||||
#define SELABEL_DB_DATATYPE 12
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _SELABEL_H_ */
|
||||
187
jni/external/selinux/restorecon.h
vendored
Normal file
187
jni/external/selinux/restorecon.h
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
#ifndef _RESTORECON_H_
|
||||
#define _RESTORECON_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* selinux_restorecon - Relabel files.
|
||||
* @pathname: specifies file/directory to relabel.
|
||||
* @restorecon_flags: specifies the actions to be performed when relabeling.
|
||||
*
|
||||
* selinux_restorecon(3) will automatically call
|
||||
* selinux_restorecon_default_handle(3) and selinux_restorecon_set_sehandle(3)
|
||||
* first time through to set the selabel_open(3) parameters to use the
|
||||
* currently loaded policy file_contexts and request their computed digest.
|
||||
*
|
||||
* Should other selabel_open(3) parameters be required see
|
||||
* selinux_restorecon_set_sehandle(3).
|
||||
*/
|
||||
extern int selinux_restorecon(const char *pathname,
|
||||
unsigned int restorecon_flags);
|
||||
/*
|
||||
* restorecon_flags options
|
||||
*/
|
||||
/*
|
||||
* Force the checking of labels even if the stored SHA1
|
||||
* digest matches the specfiles SHA1 digest.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_IGNORE_DIGEST 0x0001
|
||||
/*
|
||||
* Do not change file labels.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_NOCHANGE 0x0002
|
||||
/*
|
||||
* If set set change file label to that in spec file.
|
||||
* If not only change type component to that in spec file.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_SET_SPECFILE_CTX 0x0004
|
||||
/*
|
||||
* Recursively descend directories.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_RECURSE 0x0008
|
||||
/*
|
||||
* Log changes to selinux log. Note that if VERBOSE and
|
||||
* PROGRESS are set, then PROGRESS will take precedence.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_VERBOSE 0x0010
|
||||
/*
|
||||
* If SELINUX_RESTORECON_PROGRESS is true and
|
||||
* SELINUX_RESTORECON_MASS_RELABEL is true, then output approx % complete,
|
||||
* else output the number of files in 1k blocks processed to stdout.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_PROGRESS 0x0020
|
||||
/*
|
||||
* Convert passed-in pathname to canonical pathname.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_REALPATH 0x0040
|
||||
/*
|
||||
* Prevent descending into directories that have a different
|
||||
* device number than the pathname from which the descent began.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_XDEV 0x0080
|
||||
/*
|
||||
* Attempt to add an association between an inode and a specification.
|
||||
* If there is already an association for the inode and it conflicts
|
||||
* with the specification, then use the last matching specification.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_ADD_ASSOC 0x0100
|
||||
/*
|
||||
* Abort on errors during the file tree walk.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_ABORT_ON_ERROR 0x0200
|
||||
/*
|
||||
* Log any label changes to syslog.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_SYSLOG_CHANGES 0x0400
|
||||
/*
|
||||
* Log what spec matched each file.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_LOG_MATCHES 0x0800
|
||||
/*
|
||||
* Ignore files that do not exist.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_IGNORE_NOENTRY 0x1000
|
||||
/*
|
||||
* Do not read /proc/mounts to obtain a list of non-seclabel
|
||||
* mounts to be excluded from relabeling checks.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_IGNORE_MOUNTS 0x2000
|
||||
/*
|
||||
* Set if there is a mass relabel required.
|
||||
* See SELINUX_RESTORECON_PROGRESS flag for details.
|
||||
*/
|
||||
#define SELINUX_RESTORECON_MASS_RELABEL 0x4000
|
||||
|
||||
/**
|
||||
* selinux_restorecon_set_sehandle - Set the global fc handle.
|
||||
* @hndl: specifies handle to set as the global fc handle.
|
||||
*
|
||||
* Called by a process that has already called selabel_open(3) with it's
|
||||
* required parameters, or if selinux_restorecon_default_handle(3) has been
|
||||
* called to set the default selabel_open(3) parameters.
|
||||
*/
|
||||
// extern void selinux_restorecon_set_sehandle(struct selabel_handle *hndl);
|
||||
|
||||
/**
|
||||
* selinux_restorecon_default_handle - Sets default selabel_open(3) parameters
|
||||
* to use the currently loaded policy and
|
||||
* file_contexts, also requests the digest.
|
||||
*
|
||||
* Return value is the created handle on success or NULL with @errno set on
|
||||
* failure.
|
||||
*/
|
||||
extern struct selabel_handle *selinux_restorecon_default_handle(void);
|
||||
|
||||
/**
|
||||
* selinux_restorecon_set_exclude_list - Add a list of directories that are
|
||||
* to be excluded from relabeling.
|
||||
* @exclude_list: containing a NULL terminated list of one or more
|
||||
* directories not to be relabeled.
|
||||
*/
|
||||
extern void selinux_restorecon_set_exclude_list(const char **exclude_list);
|
||||
|
||||
/**
|
||||
* selinux_restorecon_set_alt_rootpath - Use alternate rootpath.
|
||||
* @alt_rootpath: containing the alternate rootpath to be used.
|
||||
*
|
||||
* Return %0 on success, -%1 with @errno set on failure.
|
||||
*/
|
||||
extern int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath);
|
||||
|
||||
/**
|
||||
* selinux_restorecon_xattr - Read/remove RESTORECON_LAST xattr entries.
|
||||
* @pathname: specifies directory path to check.
|
||||
* @xattr_flags: specifies the actions to be performed.
|
||||
* @xattr_list: a linked list of struct dir_xattr structures containing
|
||||
* the directory, digest and result of the action on the
|
||||
* RESTORECON_LAST entry.
|
||||
*
|
||||
* selinux_restorecon_xattr(3) will automatically call
|
||||
* selinux_restorecon_default_handle(3) and selinux_restorecon_set_sehandle(3)
|
||||
* first time through to set the selabel_open(3) parameters to use the
|
||||
* currently loaded policy file_contexts and request their computed digest.
|
||||
*
|
||||
* Should other selabel_open(3) parameters be required see
|
||||
* selinux_restorecon_set_sehandle(3), however note that a file_contexts
|
||||
* computed digest is required for selinux_restorecon_xattr().
|
||||
*/
|
||||
enum digest_result {
|
||||
MATCH = 0,
|
||||
NOMATCH,
|
||||
DELETED_MATCH,
|
||||
DELETED_NOMATCH,
|
||||
ERROR
|
||||
};
|
||||
|
||||
struct dir_xattr {
|
||||
char *directory;
|
||||
char *digest; /* A hex encoded string that can be printed. */
|
||||
enum digest_result result;
|
||||
struct dir_xattr *next;
|
||||
};
|
||||
|
||||
extern int selinux_restorecon_xattr(const char *pathname,
|
||||
unsigned int xattr_flags,
|
||||
struct dir_xattr ***xattr_list);
|
||||
|
||||
/*
|
||||
* xattr_flags options
|
||||
*/
|
||||
/* Recursively descend directories. */
|
||||
#define SELINUX_RESTORECON_XATTR_RECURSE 0x0001
|
||||
/* Delete non-matching digests from each directory in pathname. */
|
||||
#define SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS 0x0002
|
||||
/* Delete all digests found in pathname. */
|
||||
#define SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS 0x0004
|
||||
/* Do not read /proc/mounts. */
|
||||
#define SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS 0x0008
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
673
jni/external/selinux/selinux.h
vendored
Normal file
673
jni/external/selinux/selinux.h
vendored
Normal file
@@ -0,0 +1,673 @@
|
||||
#ifndef _SELINUX_H_
|
||||
#define _SELINUX_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Return 1 if we are running on a SELinux kernel, or 0 if not or -1 if we get an error. */
|
||||
extern int is_selinux_enabled(void);
|
||||
/* Return 1 if we are running on a SELinux MLS kernel, or 0 otherwise. */
|
||||
extern int is_selinux_mls_enabled(void);
|
||||
|
||||
/* No longer used; here for compatibility with legacy callers. */
|
||||
typedef char *security_context_t;
|
||||
|
||||
/* Free the memory allocated for a context by any of the below get* calls. */
|
||||
extern void freecon(char * con);
|
||||
|
||||
/* Free the memory allocated for a context array by security_compute_user. */
|
||||
extern void freeconary(char ** con);
|
||||
|
||||
/* Wrappers for the /proc/pid/attr API. */
|
||||
|
||||
/* Get current context, and set *con to refer to it.
|
||||
Caller must free via freecon. */
|
||||
extern int getcon(char ** con);
|
||||
extern int getcon_raw(char ** con);
|
||||
|
||||
/* Set the current security context to con.
|
||||
Note that use of this function requires that the entire application
|
||||
be trusted to maintain any desired separation between the old and new
|
||||
security contexts, unlike exec-based transitions performed via setexeccon.
|
||||
When possible, decompose your application and use setexeccon()+execve()
|
||||
instead. Note that the application may lose access to its open descriptors
|
||||
as a result of a setcon() unless policy allows it to use descriptors opened
|
||||
by the old context. */
|
||||
extern int setcon(const char * con);
|
||||
extern int setcon_raw(const char * con);
|
||||
|
||||
/* Get context of process identified by pid, and
|
||||
set *con to refer to it. Caller must free via freecon. */
|
||||
extern int getpidcon(pid_t pid, char ** con);
|
||||
extern int getpidcon_raw(pid_t pid, char ** con);
|
||||
|
||||
/* Get previous context (prior to last exec), and set *con to refer to it.
|
||||
Caller must free via freecon. */
|
||||
extern int getprevcon(char ** con);
|
||||
extern int getprevcon_raw(char ** con);
|
||||
|
||||
/* Get exec context, and set *con to refer to it.
|
||||
Sets *con to NULL if no exec context has been set, i.e. using default.
|
||||
If non-NULL, caller must free via freecon. */
|
||||
extern int getexeccon(char ** con);
|
||||
extern int getexeccon_raw(char ** con);
|
||||
|
||||
/* Set exec security context for the next execve.
|
||||
Call with NULL if you want to reset to the default. */
|
||||
extern int setexeccon(const char * con);
|
||||
extern int setexeccon_raw(const char * con);
|
||||
|
||||
/* Get fscreate context, and set *con to refer to it.
|
||||
Sets *con to NULL if no fs create context has been set, i.e. using default.
|
||||
If non-NULL, caller must free via freecon. */
|
||||
extern int getfscreatecon(char ** con);
|
||||
extern int getfscreatecon_raw(char ** con);
|
||||
|
||||
/* Set the fscreate security context for subsequent file creations.
|
||||
Call with NULL if you want to reset to the default. */
|
||||
extern int setfscreatecon(const char * context);
|
||||
extern int setfscreatecon_raw(const char * context);
|
||||
|
||||
/* Get keycreate context, and set *con to refer to it.
|
||||
Sets *con to NULL if no key create context has been set, i.e. using default.
|
||||
If non-NULL, caller must free via freecon. */
|
||||
extern int getkeycreatecon(char ** con);
|
||||
extern int getkeycreatecon_raw(char ** con);
|
||||
|
||||
/* Set the keycreate security context for subsequent key creations.
|
||||
Call with NULL if you want to reset to the default. */
|
||||
extern int setkeycreatecon(const char * context);
|
||||
extern int setkeycreatecon_raw(const char * context);
|
||||
|
||||
/* Get sockcreate context, and set *con to refer to it.
|
||||
Sets *con to NULL if no socket create context has been set, i.e. using default.
|
||||
If non-NULL, caller must free via freecon. */
|
||||
extern int getsockcreatecon(char ** con);
|
||||
extern int getsockcreatecon_raw(char ** con);
|
||||
|
||||
/* Set the sockcreate security context for subsequent socket creations.
|
||||
Call with NULL if you want to reset to the default. */
|
||||
extern int setsockcreatecon(const char * context);
|
||||
extern int setsockcreatecon_raw(const char * context);
|
||||
|
||||
/* Wrappers for the xattr API. */
|
||||
|
||||
/* Get file context, and set *con to refer to it.
|
||||
Caller must free via freecon. */
|
||||
extern int getfilecon(const char *path, char ** con);
|
||||
extern int getfilecon_raw(const char *path, char ** con);
|
||||
extern int lgetfilecon(const char *path, char ** con);
|
||||
extern int lgetfilecon_raw(const char *path, char ** con);
|
||||
extern int fgetfilecon(int fd, char ** con);
|
||||
extern int fgetfilecon_raw(int fd, char ** con);
|
||||
|
||||
/* Set file context */
|
||||
extern int setfilecon(const char *path, const char * con);
|
||||
extern int setfilecon_raw(const char *path, const char * con);
|
||||
extern int lsetfilecon(const char *path, const char * con);
|
||||
extern int lsetfilecon_raw(const char *path, const char * con);
|
||||
extern int fsetfilecon(int fd, const char * con);
|
||||
extern int fsetfilecon_raw(int fd, const char * con);
|
||||
|
||||
/* Wrappers for the socket API */
|
||||
|
||||
/* Get context of peer socket, and set *con to refer to it.
|
||||
Caller must free via freecon. */
|
||||
extern int getpeercon(int fd, char ** con);
|
||||
extern int getpeercon_raw(int fd, char ** con);
|
||||
|
||||
/* Wrappers for the selinuxfs (policy) API. */
|
||||
|
||||
typedef unsigned int access_vector_t;
|
||||
typedef unsigned short security_class_t;
|
||||
|
||||
struct av_decision {
|
||||
access_vector_t allowed;
|
||||
access_vector_t decided;
|
||||
access_vector_t auditallow;
|
||||
access_vector_t auditdeny;
|
||||
unsigned int seqno;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/* Definitions of av_decision.flags */
|
||||
#define SELINUX_AVD_FLAGS_PERMISSIVE 0x0001
|
||||
|
||||
/* Structure for passing options, used by AVC and label subsystems */
|
||||
struct selinux_opt {
|
||||
int type;
|
||||
const char *value;
|
||||
};
|
||||
|
||||
/* Callback facilities */
|
||||
union selinux_callback {
|
||||
/* log the printf-style format and arguments,
|
||||
with the type code indicating the type of message */
|
||||
int
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((format(printf, 2, 3)))
|
||||
#endif
|
||||
(*func_log) (int type, const char *fmt, ...);
|
||||
/* store a string representation of auditdata (corresponding
|
||||
to the given security class) into msgbuf. */
|
||||
int (*func_audit) (void *auditdata, security_class_t cls,
|
||||
char *msgbuf, size_t msgbufsize);
|
||||
/* validate the supplied context, modifying if necessary */
|
||||
int (*func_validate) (char **ctx);
|
||||
/* netlink callback for setenforce message */
|
||||
int (*func_setenforce) (int enforcing);
|
||||
/* netlink callback for policyload message */
|
||||
int (*func_policyload) (int seqno);
|
||||
};
|
||||
|
||||
#define SELINUX_CB_LOG 0
|
||||
#define SELINUX_CB_AUDIT 1
|
||||
#define SELINUX_CB_VALIDATE 2
|
||||
#define SELINUX_CB_SETENFORCE 3
|
||||
#define SELINUX_CB_POLICYLOAD 4
|
||||
|
||||
extern union selinux_callback selinux_get_callback(int type);
|
||||
extern void selinux_set_callback(int type, union selinux_callback cb);
|
||||
|
||||
/* Logging type codes, passed to the logging callback */
|
||||
#define SELINUX_ERROR 0
|
||||
#define SELINUX_WARNING 1
|
||||
#define SELINUX_INFO 2
|
||||
#define SELINUX_AVC 3
|
||||
#define SELINUX_TRANS_DIR "/var/run/setrans"
|
||||
|
||||
/* Compute an access decision. */
|
||||
extern int security_compute_av(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
access_vector_t requested,
|
||||
struct av_decision *avd);
|
||||
extern int security_compute_av_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
access_vector_t requested,
|
||||
struct av_decision *avd);
|
||||
|
||||
extern int security_compute_av_flags(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
access_vector_t requested,
|
||||
struct av_decision *avd);
|
||||
extern int security_compute_av_flags_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
access_vector_t requested,
|
||||
struct av_decision *avd);
|
||||
|
||||
/* Compute a labeling decision and set *newcon to refer to it.
|
||||
Caller must free via freecon. */
|
||||
extern int security_compute_create(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon);
|
||||
extern int security_compute_create_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon);
|
||||
extern int security_compute_create_name(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
const char *objname,
|
||||
char ** newcon);
|
||||
extern int security_compute_create_name_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
const char *objname,
|
||||
char ** newcon);
|
||||
|
||||
/* Compute a relabeling decision and set *newcon to refer to it.
|
||||
Caller must free via freecon. */
|
||||
extern int security_compute_relabel(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon);
|
||||
extern int security_compute_relabel_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon);
|
||||
|
||||
/* Compute a polyinstantiation member decision and set *newcon to refer to it.
|
||||
Caller must free via freecon. */
|
||||
extern int security_compute_member(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon);
|
||||
extern int security_compute_member_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon);
|
||||
|
||||
/* Compute the set of reachable user contexts and set *con to refer to
|
||||
the NULL-terminated array of contexts. Caller must free via freeconary. */
|
||||
extern int security_compute_user(const char * scon,
|
||||
const char *username,
|
||||
char *** con);
|
||||
extern int security_compute_user_raw(const char * scon,
|
||||
const char *username,
|
||||
char *** con);
|
||||
|
||||
/* Load a policy configuration. */
|
||||
extern int security_load_policy(void *data, size_t len);
|
||||
|
||||
/* Get the context of an initial kernel security identifier by name.
|
||||
Caller must free via freecon */
|
||||
extern int security_get_initial_context(const char *name,
|
||||
char ** con);
|
||||
extern int security_get_initial_context_raw(const char *name,
|
||||
char ** con);
|
||||
|
||||
/*
|
||||
* Make a policy image and load it.
|
||||
* This function provides a higher level interface for loading policy
|
||||
* than security_load_policy, internally determining the right policy
|
||||
* version, locating and opening the policy file, mapping it into memory,
|
||||
* manipulating it as needed for current boolean settings and/or local
|
||||
* definitions, and then calling security_load_policy to load it.
|
||||
*
|
||||
* 'preservebools' is a boolean flag indicating whether current
|
||||
* policy boolean values should be preserved into the new policy (if 1)
|
||||
* or reset to the saved policy settings (if 0). The former case is the
|
||||
* default for policy reloads, while the latter case is an option for policy
|
||||
* reloads but is primarily for the initial policy load.
|
||||
*/
|
||||
extern int selinux_mkload_policy(int preservebools);
|
||||
|
||||
/*
|
||||
* Perform the initial policy load.
|
||||
* This function determines the desired enforcing mode, sets the
|
||||
* the *enforce argument accordingly for the caller to use, sets the
|
||||
* SELinux kernel enforcing status to match it, and loads the policy.
|
||||
* It also internally handles the initial selinuxfs mount required to
|
||||
* perform these actions.
|
||||
*
|
||||
* The function returns 0 if everything including the policy load succeeds.
|
||||
* In this case, init is expected to re-exec itself in order to transition
|
||||
* to the proper security context.
|
||||
* Otherwise, the function returns -1, and init must check *enforce to
|
||||
* determine how to proceed. If enforcing (*enforce > 0), then init should
|
||||
* halt the system. Otherwise, init may proceed normally without a re-exec.
|
||||
*/
|
||||
extern int selinux_init_load_policy(int *enforce);
|
||||
|
||||
/* Translate boolean strict to name value pair. */
|
||||
typedef struct {
|
||||
char *name;
|
||||
int value;
|
||||
} SELboolean;
|
||||
/* save a list of booleans in a single transaction. */
|
||||
extern int security_set_boolean_list(size_t boolcnt,
|
||||
SELboolean * boollist, int permanent);
|
||||
|
||||
/* Load policy boolean settings.
|
||||
Path may be NULL, in which case the booleans are loaded from
|
||||
the active policy boolean configuration file. */
|
||||
extern int security_load_booleans(char *path);
|
||||
|
||||
/* Check the validity of a security context. */
|
||||
extern int security_check_context(const char * con);
|
||||
extern int security_check_context_raw(const char * con);
|
||||
|
||||
/* Canonicalize a security context. */
|
||||
extern int security_canonicalize_context(const char * con,
|
||||
char ** canoncon);
|
||||
extern int security_canonicalize_context_raw(const char * con,
|
||||
char ** canoncon);
|
||||
|
||||
/* Get the enforce flag value. */
|
||||
extern int security_getenforce(void);
|
||||
|
||||
/* Set the enforce flag value. */
|
||||
extern int security_setenforce(int value);
|
||||
|
||||
/* Get the behavior for undefined classes/permissions */
|
||||
extern int security_deny_unknown(void);
|
||||
|
||||
/* Disable SELinux at runtime (must be done prior to initial policy load). */
|
||||
extern int security_disable(void);
|
||||
|
||||
/* Get the policy version number. */
|
||||
extern int security_policyvers(void);
|
||||
|
||||
/* Get the boolean names */
|
||||
extern int security_get_boolean_names(char ***names, int *len);
|
||||
|
||||
/* Get the pending value for the boolean */
|
||||
extern int security_get_boolean_pending(const char *name);
|
||||
|
||||
/* Get the active value for the boolean */
|
||||
extern int security_get_boolean_active(const char *name);
|
||||
|
||||
/* Set the pending value for the boolean */
|
||||
extern int security_set_boolean(const char *name, int value);
|
||||
|
||||
/* Commit the pending values for the booleans */
|
||||
extern int security_commit_booleans(void);
|
||||
|
||||
/* Userspace class mapping support */
|
||||
struct security_class_mapping {
|
||||
const char *name;
|
||||
const char *perms[sizeof(access_vector_t) * 8 + 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* selinux_set_mapping - Enable dynamic mapping between integer offsets and security class names
|
||||
* @map: array of security_class_mapping structures
|
||||
*
|
||||
* The core avc_has_perm() API uses integers to represent security
|
||||
* classes; previous to the introduction of this function, it was
|
||||
* common for userspace object managers to be compiled using generated
|
||||
* offsets for a particular policy. However, that strongly ties the build of the userspace components to a particular policy.
|
||||
*
|
||||
* By using this function to map between integer offsets and security
|
||||
* class names, it's possible to replace a system policies that have
|
||||
* at least the same set of security class names as used by the
|
||||
* userspace object managers.
|
||||
*
|
||||
* To correctly use this function, you should override the generated
|
||||
* security class defines from the system policy in a local header,
|
||||
* starting at 1, and have one security_class_mapping structure entry
|
||||
* per define.
|
||||
*/
|
||||
extern int selinux_set_mapping(struct security_class_mapping *map);
|
||||
|
||||
/* Common helpers */
|
||||
|
||||
/* Convert between mode and security class values */
|
||||
extern security_class_t mode_to_security_class(mode_t mode);
|
||||
/* Convert between security class values and string names */
|
||||
extern security_class_t string_to_security_class(const char *name);
|
||||
extern const char *security_class_to_string(security_class_t cls);
|
||||
|
||||
/* Convert between individual access vector permissions and string names */
|
||||
extern const char *security_av_perm_to_string(security_class_t tclass,
|
||||
access_vector_t perm);
|
||||
extern access_vector_t string_to_av_perm(security_class_t tclass,
|
||||
const char *name);
|
||||
|
||||
/* Returns an access vector in a string representation. User must free the
|
||||
* returned string via free(). */
|
||||
extern int security_av_string(security_class_t tclass,
|
||||
access_vector_t av, char **result);
|
||||
|
||||
/* Display an access vector in a string representation. */
|
||||
extern void print_access_vector(security_class_t tclass, access_vector_t av);
|
||||
|
||||
/* Set the function used by matchpathcon_init when displaying
|
||||
errors about the file_contexts configuration. If not set,
|
||||
then this defaults to fprintf(stderr, fmt, ...). */
|
||||
extern void set_matchpathcon_printf(void (*f) (const char *fmt, ...));
|
||||
|
||||
/* Set the function used by matchpathcon_init when checking the
|
||||
validity of a context in the file contexts configuration. If not set,
|
||||
then this defaults to a test based on security_check_context().
|
||||
The function is also responsible for reporting any such error, and
|
||||
may include the 'path' and 'lineno' in such error messages. */
|
||||
extern void set_matchpathcon_invalidcon(int (*f) (const char *path,
|
||||
unsigned lineno,
|
||||
char *context));
|
||||
|
||||
/* Same as above, but also allows canonicalization of the context,
|
||||
by changing *context to refer to the canonical form. If not set,
|
||||
and invalidcon is also not set, then this defaults to calling
|
||||
security_canonicalize_context(). */
|
||||
extern void set_matchpathcon_canoncon(int (*f) (const char *path,
|
||||
unsigned lineno,
|
||||
char **context));
|
||||
|
||||
/* Set flags controlling operation of matchpathcon_init or matchpathcon. */
|
||||
#define MATCHPATHCON_BASEONLY 1 /* Only process the base file_contexts file. */
|
||||
#define MATCHPATHCON_NOTRANS 2 /* Do not perform any context translation. */
|
||||
#define MATCHPATHCON_VALIDATE 4 /* Validate/canonicalize contexts at init time. */
|
||||
extern void set_matchpathcon_flags(unsigned int flags);
|
||||
|
||||
/* Load the file contexts configuration specified by 'path'
|
||||
into memory for use by subsequent matchpathcon calls.
|
||||
If 'path' is NULL, then load the active file contexts configuration,
|
||||
i.e. the path returned by selinux_file_context_path().
|
||||
Unless the MATCHPATHCON_BASEONLY flag has been set, this
|
||||
function also checks for a 'path'.homedirs file and
|
||||
a 'path'.local file and loads additional specifications
|
||||
from them if present. */
|
||||
extern int matchpathcon_init(const char *path);
|
||||
|
||||
/* Same as matchpathcon_init, but only load entries with
|
||||
regexes that have stems that are prefixes of 'prefix'. */
|
||||
extern int matchpathcon_init_prefix(const char *path, const char *prefix);
|
||||
|
||||
/* Free the memory allocated by matchpathcon_init. */
|
||||
extern void matchpathcon_fini(void);
|
||||
|
||||
/* Resolve all of the symlinks and relative portions of a pathname, but NOT
|
||||
* the final component (same a realpath() unless the final component is a
|
||||
* symlink. Resolved path must be a path of size PATH_MAX + 1 */
|
||||
extern int realpath_not_final(const char *name, char *resolved_path);
|
||||
|
||||
/* Match the specified pathname and mode against the file contexts
|
||||
configuration and set *con to refer to the resulting context.
|
||||
'mode' can be 0 to disable mode matching.
|
||||
Caller must free via freecon.
|
||||
If matchpathcon_init has not already been called, then this function
|
||||
will call it upon its first invocation with a NULL path. */
|
||||
extern int matchpathcon(const char *path,
|
||||
mode_t mode, char ** con);
|
||||
|
||||
/* Same as above, but return a specification index for
|
||||
later use in a matchpathcon_filespec_add() call - see below. */
|
||||
extern int matchpathcon_index(const char *path,
|
||||
mode_t mode, char ** con);
|
||||
|
||||
/* Maintain an association between an inode and a specification index,
|
||||
and check whether a conflicting specification is already associated
|
||||
with the same inode (e.g. due to multiple hard links). If so, then
|
||||
use the latter of the two specifications based on their order in the
|
||||
file contexts configuration. Return the used specification index. */
|
||||
extern int matchpathcon_filespec_add(ino_t ino, int specind, const char *file);
|
||||
|
||||
/* Destroy any inode associations that have been added, e.g. to restart
|
||||
for a new filesystem. */
|
||||
extern void matchpathcon_filespec_destroy(void);
|
||||
|
||||
/* Display statistics on the hash table usage for the associations. */
|
||||
extern void matchpathcon_filespec_eval(void);
|
||||
|
||||
/* Check to see whether any specifications had no matches and report them.
|
||||
The 'str' is used as a prefix for any warning messages. */
|
||||
extern void matchpathcon_checkmatches(char *str);
|
||||
|
||||
/* Match the specified media and against the media contexts
|
||||
configuration and set *con to refer to the resulting context.
|
||||
Caller must free con via freecon. */
|
||||
extern int matchmediacon(const char *media, char ** con);
|
||||
|
||||
/*
|
||||
selinux_getenforcemode reads the /etc/selinux/config file and determines
|
||||
whether the machine should be started in enforcing (1), permissive (0) or
|
||||
disabled (-1) mode.
|
||||
*/
|
||||
extern int selinux_getenforcemode(int *enforce);
|
||||
|
||||
/*
|
||||
selinux_boolean_sub reads the /etc/selinux/TYPE/booleans.subs_dist file
|
||||
looking for a record with boolean_name. If a record exists selinux_boolean_sub
|
||||
returns the translated name otherwise it returns the original name.
|
||||
The returned value needs to be freed. On failure NULL will be returned.
|
||||
*/
|
||||
extern char *selinux_boolean_sub(const char *boolean_name);
|
||||
|
||||
/*
|
||||
selinux_getpolicytype reads the /etc/selinux/config file and determines
|
||||
what the default policy for the machine is. Calling application must
|
||||
free policytype.
|
||||
*/
|
||||
extern int selinux_getpolicytype(char **policytype);
|
||||
|
||||
/*
|
||||
selinux_policy_root reads the /etc/selinux/config file and returns
|
||||
the directory path under which the compiled policy file and context
|
||||
configuration files exist.
|
||||
*/
|
||||
extern const char *selinux_policy_root(void);
|
||||
|
||||
/*
|
||||
selinux_set_policy_root sets an alternate policy root directory path under
|
||||
which the compiled policy file and context configuration files exist.
|
||||
*/
|
||||
extern int selinux_set_policy_root(const char *rootpath);
|
||||
|
||||
/* These functions return the paths to specific files under the
|
||||
policy root directory. */
|
||||
extern const char *selinux_current_policy_path(void);
|
||||
extern const char *selinux_binary_policy_path(void);
|
||||
extern const char *selinux_failsafe_context_path(void);
|
||||
extern const char *selinux_removable_context_path(void);
|
||||
extern const char *selinux_default_context_path(void);
|
||||
extern const char *selinux_user_contexts_path(void);
|
||||
extern const char *selinux_file_context_path(void);
|
||||
extern const char *selinux_file_context_homedir_path(void);
|
||||
extern const char *selinux_file_context_local_path(void);
|
||||
extern const char *selinux_file_context_subs_path(void);
|
||||
extern const char *selinux_file_context_subs_dist_path(void);
|
||||
extern const char *selinux_homedir_context_path(void);
|
||||
extern const char *selinux_media_context_path(void);
|
||||
extern const char *selinux_virtual_domain_context_path(void);
|
||||
extern const char *selinux_virtual_image_context_path(void);
|
||||
extern const char *selinux_lxc_contexts_path(void);
|
||||
extern const char *selinux_x_context_path(void);
|
||||
extern const char *selinux_sepgsql_context_path(void);
|
||||
extern const char *selinux_openrc_contexts_path(void);
|
||||
extern const char *selinux_openssh_contexts_path(void);
|
||||
extern const char *selinux_snapperd_contexts_path(void);
|
||||
extern const char *selinux_systemd_contexts_path(void);
|
||||
extern const char *selinux_contexts_path(void);
|
||||
extern const char *selinux_securetty_types_path(void);
|
||||
extern const char *selinux_booleans_subs_path(void);
|
||||
extern const char *selinux_booleans_path(void);
|
||||
extern const char *selinux_customizable_types_path(void);
|
||||
extern const char *selinux_users_path(void);
|
||||
extern const char *selinux_usersconf_path(void);
|
||||
extern const char *selinux_translations_path(void);
|
||||
extern const char *selinux_colors_path(void);
|
||||
extern const char *selinux_netfilter_context_path(void);
|
||||
extern const char *selinux_path(void);
|
||||
|
||||
/**
|
||||
* selinux_check_access - Check permissions and perform appropriate auditing.
|
||||
* @scon: source security context
|
||||
* @tcon: target security context
|
||||
* @tclass: target security class string
|
||||
* @perm: requested permissions string, interpreted based on @tclass
|
||||
* @auditdata: auxiliary audit data
|
||||
*
|
||||
* Check the AVC to determine whether the @perm permissions are granted
|
||||
* for the SID pair (@scon, @tcon), interpreting the permissions
|
||||
* based on @tclass.
|
||||
* Return %0 if all @perm permissions are granted, -%1 with
|
||||
* @errno set to %EACCES if any permissions are denied or to another
|
||||
* value upon other errors.
|
||||
* If auditing or logging is configured the appropriate callbacks will be called
|
||||
* and passed the auditdata field
|
||||
*/
|
||||
extern int selinux_check_access(const char * scon, const char * tcon, const char *tclass, const char *perm, void *auditdata);
|
||||
|
||||
/* Check a permission in the passwd class.
|
||||
Return 0 if granted or -1 otherwise. */
|
||||
extern int selinux_check_passwd_access(access_vector_t requested);
|
||||
extern int checkPasswdAccess(access_vector_t requested);
|
||||
|
||||
/* Check if the tty_context is defined as a securetty
|
||||
Return 0 if secure, < 0 otherwise. */
|
||||
extern int selinux_check_securetty_context(const char * tty_context);
|
||||
|
||||
/* Set the path to the selinuxfs mount point explicitly.
|
||||
Normally, this is determined automatically during libselinux
|
||||
initialization, but this is not always possible, e.g. for /sbin/init
|
||||
which performs the initial mount of selinuxfs. */
|
||||
void set_selinuxmnt(const char *mnt);
|
||||
|
||||
/* Check if selinuxfs exists as a kernel filesystem */
|
||||
int selinuxfs_exists(void);
|
||||
|
||||
/* clear selinuxmnt variable and free allocated memory */
|
||||
void fini_selinuxmnt(void);
|
||||
|
||||
/* Set an appropriate security context based on the filename of a helper
|
||||
* program, falling back to a new context with the specified type. */
|
||||
extern int setexecfilecon(const char *filename, const char *fallback_type);
|
||||
|
||||
#ifndef DISABLE_RPM
|
||||
/* Execute a helper for rpm in an appropriate security context. */
|
||||
extern int rpm_execcon(unsigned int verified,
|
||||
const char *filename,
|
||||
char *const argv[], char *const envp[]);
|
||||
#endif
|
||||
|
||||
/* Returns whether a file context is customizable, and should not
|
||||
be relabeled . */
|
||||
extern int is_context_customizable(const char * scontext);
|
||||
|
||||
/* Perform context translation between the human-readable format
|
||||
("translated") and the internal system format ("raw").
|
||||
Caller must free the resulting context via freecon.
|
||||
Returns -1 upon an error or 0 otherwise.
|
||||
If passed NULL, sets the returned context to NULL and returns 0. */
|
||||
extern int selinux_trans_to_raw_context(const char * trans,
|
||||
char ** rawp);
|
||||
extern int selinux_raw_to_trans_context(const char * raw,
|
||||
char ** transp);
|
||||
|
||||
/* Perform context translation between security contexts
|
||||
and display colors. Returns a space-separated list of ten
|
||||
ten hex RGB triples prefixed by hash marks, e.g. "#ff0000".
|
||||
Caller must free the resulting string via free.
|
||||
Returns -1 upon an error or 0 otherwise. */
|
||||
extern int selinux_raw_context_to_color(const char * raw,
|
||||
char **color_str);
|
||||
|
||||
/* Get the SELinux username and level to use for a given Linux username.
|
||||
These values may then be passed into the get_ordered_context_list*
|
||||
and get_default_context* functions to obtain a context for the user.
|
||||
Returns 0 on success or -1 otherwise.
|
||||
Caller must free the returned strings via free. */
|
||||
extern int getseuserbyname(const char *linuxuser, char **seuser, char **level);
|
||||
|
||||
/* Get the SELinux username and level to use for a given Linux username and service.
|
||||
These values may then be passed into the get_ordered_context_list*
|
||||
and get_default_context* functions to obtain a context for the user.
|
||||
Returns 0 on success or -1 otherwise.
|
||||
Caller must free the returned strings via free. */
|
||||
extern int getseuser(const char *username, const char *service,
|
||||
char **r_seuser, char **r_level);
|
||||
|
||||
/* Compare two file contexts, return 0 if equivalent. */
|
||||
extern int selinux_file_context_cmp(const char * a,
|
||||
const char * b);
|
||||
|
||||
/*
|
||||
* Verify the context of the file 'path' against policy.
|
||||
* Return 1 if match, 0 if not and -1 on error.
|
||||
*/
|
||||
extern int selinux_file_context_verify(const char *path, mode_t mode);
|
||||
|
||||
/* This function sets the file context on to the system defaults returns 0 on success */
|
||||
extern int selinux_lsetfilecon_default(const char *path);
|
||||
|
||||
/*
|
||||
* Force a reset of the loaded configuration
|
||||
* WARNING: This is not thread safe. Be very sure that no other threads
|
||||
* are calling into libselinux when this is called.
|
||||
*/
|
||||
extern void selinux_reset_config(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
355
jni/external/selinux_stub.c
vendored
Normal file
355
jni/external/selinux_stub.c
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
#include <stdbool.h>
|
||||
#include "selinux/avc.h"
|
||||
#include "selinux/context.h"
|
||||
#include "selinux/get_context_list.h"
|
||||
#include "selinux/get_default_type.h"
|
||||
#include "selinux/restorecon.h"
|
||||
#include "selinux/selinux.h"
|
||||
int is_selinux_enabled(void) { return 0; }
|
||||
int is_selinux_mls_enabled(void) { return 0; }
|
||||
void freecon(char * con) { }
|
||||
void freeconary(char ** con) { }
|
||||
int getcon(char ** con) { return 0; }
|
||||
int getcon_raw(char ** con) { return 0; }
|
||||
int setcon(const char * con) { return 0; }
|
||||
int setcon_raw(const char * con) { return 0; }
|
||||
int getpidcon(pid_t pid, char ** con) { return 0; }
|
||||
int getpidcon_raw(pid_t pid, char ** con) { return 0; }
|
||||
int getprevcon(char ** con) { return 0; }
|
||||
int getprevcon_raw(char ** con) { return 0; }
|
||||
int getexeccon(char ** con) { return 0; }
|
||||
int getexeccon_raw(char ** con) { return 0; }
|
||||
int setexeccon(const char * con) { return 0; }
|
||||
int setexeccon_raw(const char * con) { return 0; }
|
||||
int getfscreatecon(char ** con) { return 0; }
|
||||
int getfscreatecon_raw(char ** con) { return 0; }
|
||||
int setfscreatecon(const char * context) { return 0; }
|
||||
int setfscreatecon_raw(const char * context) { return 0; }
|
||||
int getkeycreatecon(char ** con) { return 0; }
|
||||
int getkeycreatecon_raw(char ** con) { return 0; }
|
||||
int setkeycreatecon(const char * context) { return 0; }
|
||||
int setkeycreatecon_raw(const char * context) { return 0; }
|
||||
int getsockcreatecon(char ** con) { return 0; }
|
||||
int getsockcreatecon_raw(char ** con) { return 0; }
|
||||
int setsockcreatecon(const char * context) { return 0; }
|
||||
int setsockcreatecon_raw(const char * context) { return 0; }
|
||||
int getfilecon(const char *path, char ** con) { return 0; }
|
||||
int getfilecon_raw(const char *path, char ** con) { return 0; }
|
||||
int lgetfilecon(const char *path, char ** con) { return 0; }
|
||||
int lgetfilecon_raw(const char *path, char ** con) { return 0; }
|
||||
int fgetfilecon(int fd, char ** con) { return 0; }
|
||||
int fgetfilecon_raw(int fd, char ** con) { return 0; }
|
||||
int setfilecon(const char *path, const char * con) { return 0; }
|
||||
int setfilecon_raw(const char *path, const char * con) { return 0; }
|
||||
int lsetfilecon(const char *path, const char * con) { return 0; }
|
||||
int lsetfilecon_raw(const char *path, const char * con) { return 0; }
|
||||
int fsetfilecon(int fd, const char * con) { return 0; }
|
||||
int fsetfilecon_raw(int fd, const char * con) { return 0; }
|
||||
int getpeercon(int fd, char ** con) { return 0; }
|
||||
int getpeercon_raw(int fd, char ** con) { return 0; }
|
||||
void selinux_set_callback(int type, union selinux_callback cb) { }
|
||||
int security_compute_av(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
access_vector_t requested,
|
||||
struct av_decision *avd) { return 0; }
|
||||
int security_compute_av_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
access_vector_t requested,
|
||||
struct av_decision *avd) { return 0; }
|
||||
int security_compute_av_flags(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
access_vector_t requested,
|
||||
struct av_decision *avd) { return 0; }
|
||||
int security_compute_av_flags_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
access_vector_t requested,
|
||||
struct av_decision *avd) { return 0; }
|
||||
int security_compute_create(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon) { return 0; }
|
||||
int security_compute_create_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon) { return 0; }
|
||||
int security_compute_create_name(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
const char *objname,
|
||||
char ** newcon) { return 0; }
|
||||
int security_compute_create_name_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
const char *objname,
|
||||
char ** newcon) { return 0; }
|
||||
int security_compute_relabel(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon) { return 0; }
|
||||
int security_compute_relabel_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon) { return 0; }
|
||||
int security_compute_member(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon) { return 0; }
|
||||
int security_compute_member_raw(const char * scon,
|
||||
const char * tcon,
|
||||
security_class_t tclass,
|
||||
char ** newcon) { return 0; }
|
||||
int security_compute_user(const char * scon,
|
||||
const char *username,
|
||||
char *** con) { return 0; }
|
||||
int security_compute_user_raw(const char * scon,
|
||||
const char *username,
|
||||
char *** con) { return 0; }
|
||||
int security_load_policy(void *data, size_t len) { return 0; }
|
||||
int security_get_initial_context(const char *name,
|
||||
char ** con) { return 0; }
|
||||
int security_get_initial_context_raw(const char *name,
|
||||
char ** con) { return 0; }
|
||||
int selinux_mkload_policy(int preservebools) { return 0; }
|
||||
int selinux_init_load_policy(int *enforce) { return 0; }
|
||||
int security_set_boolean_list(size_t boolcnt,
|
||||
SELboolean * boollist, int permanent) { return 0; }
|
||||
int security_load_booleans(char *path) { return 0; }
|
||||
int security_check_context(const char * con) { return 0; }
|
||||
int security_check_context_raw(const char * con) { return 0; }
|
||||
int security_canonicalize_context(const char * con,
|
||||
char ** canoncon) { return 0; }
|
||||
int security_canonicalize_context_raw(const char * con,
|
||||
char ** canoncon) { return 0; }
|
||||
int security_getenforce(void) { return 0; }
|
||||
int security_setenforce(int value) { return 0; }
|
||||
int security_deny_unknown(void) { return 0; }
|
||||
int security_disable(void) { return 0; }
|
||||
int security_policyvers(void) { return 0; }
|
||||
int security_get_boolean_names(char ***names, int *len) { return 0; }
|
||||
int security_get_boolean_pending(const char *name) { return 0; }
|
||||
int security_get_boolean_active(const char *name) { return 0; }
|
||||
int security_set_boolean(const char *name, int value) { return 0; }
|
||||
int security_commit_booleans(void) { return 0; }
|
||||
int selinux_set_mapping(struct security_class_mapping *map) { return 0; }
|
||||
security_class_t mode_to_security_class(mode_t mode) { return 0; }
|
||||
security_class_t string_to_security_class(const char *name) { return 0; }
|
||||
const char *security_class_to_string(security_class_t cls) { return 0; }
|
||||
const char *security_av_perm_to_string(security_class_t tclass,
|
||||
access_vector_t perm) { return 0; }
|
||||
access_vector_t string_to_av_perm(security_class_t tclass,
|
||||
const char *name) { return 0; }
|
||||
int security_av_string(security_class_t tclass,
|
||||
access_vector_t av, char **result) { return 0; }
|
||||
void print_access_vector(security_class_t tclass, access_vector_t av) { }
|
||||
void set_matchpathcon_printf(void (*f) (const char *fmt, ...)) { }
|
||||
void set_matchpathcon_invalidcon(int (*f) (const char *path,
|
||||
unsigned lineno,
|
||||
char *context)) { }
|
||||
void set_matchpathcon_canoncon(int (*f) (const char *path,
|
||||
unsigned lineno,
|
||||
char **context)) { }
|
||||
void set_matchpathcon_flags(unsigned int flags) { }
|
||||
int matchpathcon_init(const char *path) { return 0; }
|
||||
int matchpathcon_init_prefix(const char *path, const char *prefix) { return 0; }
|
||||
void matchpathcon_fini(void) { }
|
||||
int realpath_not_final(const char *name, char *resolved_path) { return 0; }
|
||||
int matchpathcon(const char *path,
|
||||
mode_t mode, char ** con) { return 0; }
|
||||
int matchpathcon_index(const char *path,
|
||||
mode_t mode, char ** con) { return 0; }
|
||||
int matchpathcon_filespec_add(ino_t ino, int specind, const char *file) { return 0; }
|
||||
void matchpathcon_filespec_destroy(void) { }
|
||||
void matchpathcon_filespec_eval(void) { }
|
||||
void matchpathcon_checkmatches(char *str) { }
|
||||
int matchmediacon(const char *media, char ** con) { return 0; }
|
||||
int selinux_getenforcemode(int *enforce) { return 0; }
|
||||
char *selinux_boolean_sub(const char *boolean_name) { return 0; }
|
||||
int selinux_getpolicytype(char **policytype) { return 0; }
|
||||
const char *selinux_policy_root(void) { return 0; }
|
||||
int selinux_set_policy_root(const char *rootpath) { return 0; }
|
||||
const char *selinux_current_policy_path(void) { return 0; }
|
||||
const char *selinux_binary_policy_path(void) { return 0; }
|
||||
const char *selinux_failsafe_context_path(void) { return 0; }
|
||||
const char *selinux_removable_context_path(void) { return 0; }
|
||||
const char *selinux_default_context_path(void) { return 0; }
|
||||
const char *selinux_user_contexts_path(void) { return 0; }
|
||||
const char *selinux_file_context_path(void) { return 0; }
|
||||
const char *selinux_file_context_homedir_path(void) { return 0; }
|
||||
const char *selinux_file_context_local_path(void) { return 0; }
|
||||
const char *selinux_file_context_subs_path(void) { return 0; }
|
||||
const char *selinux_file_context_subs_dist_path(void) { return 0; }
|
||||
const char *selinux_homedir_context_path(void) { return 0; }
|
||||
const char *selinux_media_context_path(void) { return 0; }
|
||||
const char *selinux_virtual_domain_context_path(void) { return 0; }
|
||||
const char *selinux_virtual_image_context_path(void) { return 0; }
|
||||
const char *selinux_lxc_contexts_path(void) { return 0; }
|
||||
const char *selinux_x_context_path(void) { return 0; }
|
||||
const char *selinux_sepgsql_context_path(void) { return 0; }
|
||||
const char *selinux_openrc_contexts_path(void) { return 0; }
|
||||
const char *selinux_openssh_contexts_path(void) { return 0; }
|
||||
const char *selinux_snapperd_contexts_path(void) { return 0; }
|
||||
const char *selinux_systemd_contexts_path(void) { return 0; }
|
||||
const char *selinux_contexts_path(void) { return 0; }
|
||||
const char *selinux_securetty_types_path(void) { return 0; }
|
||||
const char *selinux_booleans_subs_path(void) { return 0; }
|
||||
const char *selinux_booleans_path(void) { return 0; }
|
||||
const char *selinux_customizable_types_path(void) { return 0; }
|
||||
const char *selinux_users_path(void) { return 0; }
|
||||
const char *selinux_usersconf_path(void) { return 0; }
|
||||
const char *selinux_translations_path(void) { return 0; }
|
||||
const char *selinux_colors_path(void) { return 0; }
|
||||
const char *selinux_netfilter_context_path(void) { return 0; }
|
||||
const char *selinux_path(void) { return 0; }
|
||||
int selinux_check_access(const char * scon, const char * tcon, const char *tclass, const char *perm, void *auditdata) { return 0; }
|
||||
int selinux_check_passwd_access(access_vector_t requested) { return 0; }
|
||||
int checkPasswdAccess(access_vector_t requested) { return 0; }
|
||||
int selinux_check_securetty_context(const char * tty_context) { return 0; }
|
||||
void set_selinuxmnt(const char *mnt) { }
|
||||
int selinuxfs_exists(void) { return 0; }
|
||||
void fini_selinuxmnt(void) {}
|
||||
int setexecfilecon(const char *filename, const char *fallback_type) { return 0; }
|
||||
#ifndef DISABLE_RPM
|
||||
int rpm_execcon(unsigned int verified,
|
||||
const char *filename,
|
||||
char *const argv[], char *const envp[]) { return 0; }
|
||||
#endif
|
||||
int is_context_customizable(const char * scontext) { return 0; }
|
||||
int selinux_trans_to_raw_context(const char * trans,
|
||||
char ** rawp) { return 0; }
|
||||
int selinux_raw_to_trans_context(const char * raw,
|
||||
char ** transp) { return 0; }
|
||||
int selinux_raw_context_to_color(const char * raw,
|
||||
char **color_str) { return 0; }
|
||||
int getseuserbyname(const char *linuxuser, char **seuser, char **level) { return 0; }
|
||||
int getseuser(const char *username, const char *service,
|
||||
char **r_seuser, char **r_level) { return 0; }
|
||||
int selinux_file_context_cmp(const char * a,
|
||||
const char * b) { return 0; }
|
||||
int selinux_file_context_verify(const char *path, mode_t mode) { return 0; }
|
||||
int selinux_lsetfilecon_default(const char *path) { return 0; }
|
||||
void selinux_reset_config(void) { }
|
||||
int avc_sid_to_context(security_id_t sid, char ** ctx) { return 0; }
|
||||
int avc_sid_to_context_raw(security_id_t sid, char ** ctx) { return 0; }
|
||||
int avc_context_to_sid(const char * ctx, security_id_t * sid) { return 0; }
|
||||
int avc_context_to_sid_raw(const char * ctx, security_id_t * sid) { return 0; }
|
||||
int sidget(security_id_t sid) { return 0; }
|
||||
int sidput(security_id_t sid) { return 0; }
|
||||
int avc_get_initial_sid(const char *name, security_id_t * sid) { return 0; }
|
||||
int avc_init(const char *msgprefix,
|
||||
const struct avc_memory_callback *mem_callbacks,
|
||||
const struct avc_log_callback *log_callbacks,
|
||||
const struct avc_thread_callback *thread_callbacks,
|
||||
const struct avc_lock_callback *lock_callbacks) { return 0; }
|
||||
int avc_open(struct selinux_opt *opts, unsigned nopts) { return 0; }
|
||||
void avc_cleanup(void) { }
|
||||
int avc_reset(void) { return 0; }
|
||||
void avc_destroy(void) { }
|
||||
int avc_has_perm_noaudit(security_id_t ssid,
|
||||
security_id_t tsid,
|
||||
security_class_t tclass,
|
||||
access_vector_t requested,
|
||||
struct avc_entry_ref *aeref, struct av_decision *avd) { return 0; }
|
||||
int avc_has_perm(security_id_t ssid, security_id_t tsid,
|
||||
security_class_t tclass, access_vector_t requested,
|
||||
struct avc_entry_ref *aeref, void *auditdata) { return 0; }
|
||||
void avc_audit(security_id_t ssid, security_id_t tsid,
|
||||
security_class_t tclass, access_vector_t requested,
|
||||
struct av_decision *avd, int result, void *auditdata) { }
|
||||
int avc_compute_create(security_id_t ssid,
|
||||
security_id_t tsid,
|
||||
security_class_t tclass, security_id_t * newsid) { return 0; }
|
||||
int avc_compute_member(security_id_t ssid,
|
||||
security_id_t tsid,
|
||||
security_class_t tclass, security_id_t * newsid) { return 0; }
|
||||
int avc_add_callback(int (*callback)
|
||||
(uint32_t event, security_id_t ssid,
|
||||
security_id_t tsid, security_class_t tclass,
|
||||
access_vector_t perms,
|
||||
access_vector_t * out_retained),
|
||||
uint32_t events, security_id_t ssid,
|
||||
security_id_t tsid, security_class_t tclass,
|
||||
access_vector_t perms) { return 0; }
|
||||
void avc_cache_stats(struct avc_cache_stats *stats) { }
|
||||
void avc_av_stats(void) { }
|
||||
void avc_sid_stats(void) { }
|
||||
int avc_netlink_open(int blocking) { return 0; }
|
||||
void avc_netlink_loop(void) { }
|
||||
void avc_netlink_close(void) { }
|
||||
int avc_netlink_acquire_fd(void) { return 0; }
|
||||
void avc_netlink_release_fd(void) { }
|
||||
int avc_netlink_check_nb(void) { return 0; }
|
||||
int selinux_status_open(int fallback) { return 0; }
|
||||
void selinux_status_close(void) { }
|
||||
int selinux_status_updated(void) { return 0; }
|
||||
int selinux_status_getenforce(void) { return 0; }
|
||||
int selinux_status_policyload(void) { return 0; }
|
||||
int selinux_status_deny_unknown(void) { return 0; }
|
||||
context_t context_new(const char *s) { return 0; }
|
||||
char *context_str(context_t c) { return 0; }
|
||||
void context_free(context_t c) { }
|
||||
const char *context_type_get(context_t c) { return 0; }
|
||||
const char *context_range_get(context_t c) { return 0; }
|
||||
const char *context_role_get(context_t c) { return 0; }
|
||||
const char *context_user_get(context_t c) { return 0; }
|
||||
int context_type_set(context_t c, const char *s) { return 0; }
|
||||
int context_range_set(context_t c, const char *s) { return 0; }
|
||||
int context_role_set(context_t c, const char *s) { return 0; }
|
||||
int context_user_set(context_t c, const char *s) { return 0; }
|
||||
int get_ordered_context_list(const char *user,
|
||||
char * fromcon,
|
||||
char *** list) { return 0; }
|
||||
int get_ordered_context_list_with_level(const char *user,
|
||||
const char *level,
|
||||
char * fromcon,
|
||||
char *** list) { return 0; }
|
||||
int get_default_context(const char *user,
|
||||
char * fromcon,
|
||||
char ** newcon) { return 0; }
|
||||
int get_default_context_with_level(const char *user,
|
||||
const char *level,
|
||||
char * fromcon,
|
||||
char ** newcon) { return 0; }
|
||||
int get_default_context_with_role(const char *user,
|
||||
const char *role,
|
||||
char * fromcon,
|
||||
char ** newcon) { return 0; }
|
||||
int get_default_context_with_rolelevel(const char *user,
|
||||
const char *role,
|
||||
const char *level,
|
||||
char * fromcon,
|
||||
char ** newcon) { return 0; }
|
||||
int query_user_context(char ** list,
|
||||
char ** newcon) { return 0; }
|
||||
int manual_user_enter_context(const char *user,
|
||||
char ** newcon) { return 0; }
|
||||
const char *selinux_default_type_path(void) { return 0; }
|
||||
int get_default_type(const char *role, char **type) { return 0; }
|
||||
struct selabel_handle *selabel_open(unsigned int backend,
|
||||
const struct selinux_opt *opts,
|
||||
unsigned nopts) { return 0; }
|
||||
void selabel_close(struct selabel_handle *handle) { }
|
||||
int selabel_lookup(struct selabel_handle *handle, char **con,
|
||||
const char *key, int type) { return 0; }
|
||||
int selabel_lookup_raw(struct selabel_handle *handle, char **con,
|
||||
const char *key, int type) { return 0; }
|
||||
bool selabel_partial_match(struct selabel_handle *handle, const char *key) { return 0; }
|
||||
int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
|
||||
const char *key, const char **aliases, int type) { return 0; }
|
||||
int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con,
|
||||
const char *key, const char **aliases, int type) { return 0; }
|
||||
int selabel_digest(struct selabel_handle *rec,
|
||||
unsigned char **digest, size_t *digest_len,
|
||||
char ***specfiles, size_t *num_specfiles) { return 0; }
|
||||
void selabel_stats(struct selabel_handle *handle) { }
|
||||
int selinux_restorecon(const char *pathname,
|
||||
unsigned int restorecon_flags) { return 0; }
|
||||
struct selabel_handle *selinux_restorecon_default_handle(void) { return 0; }
|
||||
void selinux_restorecon_set_exclude_list(const char **exclude_list) { }
|
||||
int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath) { return 0; }
|
||||
int selinux_restorecon_xattr(const char *pathname,
|
||||
unsigned int xattr_flags,
|
||||
struct dir_xattr ***xattr_list) { return 0; }
|
||||
10491
jni/external/sqlite3.h
vendored
Normal file
10491
jni/external/sqlite3.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
676
jni/external/sqlite3_stub.c
vendored
Normal file
676
jni/external/sqlite3_stub.c
vendored
Normal file
@@ -0,0 +1,676 @@
|
||||
#include "sqlite3.h"
|
||||
SQLITE_API const char *sqlite3_libversion(void) { return 0; }
|
||||
SQLITE_API const char *sqlite3_sourceid(void) { return 0; }
|
||||
SQLITE_API int sqlite3_libversion_number(void) { return 0; }
|
||||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
SQLITE_API int sqlite3_compileoption_used(const char *zOptName) { return 0; }
|
||||
SQLITE_API const char *sqlite3_compileoption_get(int N) { return 0; }
|
||||
#endif
|
||||
SQLITE_API int sqlite3_threadsafe(void) { return 0; }
|
||||
SQLITE_API int sqlite3_close(sqlite3 *db) { return 0; }
|
||||
SQLITE_API int sqlite3_close_v2(sqlite3 *db) { return 0; }
|
||||
SQLITE_API int sqlite3_exec(
|
||||
sqlite3 *db,
|
||||
const char *sql,
|
||||
int (*callback)(void*,int,char**,char**),
|
||||
void *v,
|
||||
char **errmsg
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_initialize(void) { return 0; }
|
||||
SQLITE_API int sqlite3_shutdown(void) { return 0; }
|
||||
SQLITE_API int sqlite3_os_init(void) { return 0; }
|
||||
SQLITE_API int sqlite3_os_end(void) { return 0; }
|
||||
SQLITE_API int sqlite3_config(int i, ...) { return 0; }
|
||||
SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...) { return 0; }
|
||||
SQLITE_API int sqlite3_extended_result_codes(sqlite3 *db, int onoff) { return 0; }
|
||||
SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3 *db) { return 0; }
|
||||
SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3 *db,sqlite3_int64 i) { }
|
||||
SQLITE_API int sqlite3_changes(sqlite3 *db) { return 0; }
|
||||
SQLITE_API int sqlite3_total_changes(sqlite3 *db) { return 0; }
|
||||
SQLITE_API void sqlite3_interrupt(sqlite3 *db) { }
|
||||
SQLITE_API int sqlite3_complete(const char *sql) { return 0; }
|
||||
SQLITE_API int sqlite3_complete16(const void *sql) { return 0; }
|
||||
SQLITE_API int sqlite3_busy_handler(sqlite3 *db, int(*f) (void *v,int i), void* v) { return 0; }
|
||||
SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms) { return 0; }
|
||||
SQLITE_API int sqlite3_get_table(
|
||||
sqlite3 *db,
|
||||
const char *zSql,
|
||||
char ***pazResult,
|
||||
int *pnRow,
|
||||
int *pnColumn,
|
||||
char **pzErrmsg
|
||||
) { return 0; }
|
||||
SQLITE_API void sqlite3_free_table(char **result) { }
|
||||
SQLITE_API char *sqlite3_mprintf(const char* s,...) { return 0; }
|
||||
SQLITE_API char *sqlite3_vmprintf(const char* s, va_list v) { return 0; }
|
||||
SQLITE_API char *sqlite3_snprintf(int i, char* s, const char* st, ...) { return 0; }
|
||||
SQLITE_API char *sqlite3_vsnprintf(int i, char* s, const char* st, va_list v) { return 0; }
|
||||
SQLITE_API void *sqlite3_malloc(int i) { return 0; }
|
||||
SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 i) { return 0; }
|
||||
SQLITE_API void *sqlite3_realloc(void* v, int i) { return 0; }
|
||||
SQLITE_API void *sqlite3_realloc64(void* v, sqlite3_uint64 i) { return 0; }
|
||||
SQLITE_API void sqlite3_free(void* v) { }
|
||||
SQLITE_API sqlite3_uint64 sqlite3_msize(void* v) { return 0; }
|
||||
SQLITE_API sqlite3_int64 sqlite3_memory_used(void) { return 0; }
|
||||
SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag) { return 0; }
|
||||
SQLITE_API void sqlite3_randomness(int N, void *P) { }
|
||||
SQLITE_API int sqlite3_set_authorizer(
|
||||
sqlite3 *db,
|
||||
int (*xAuth)(void *v,int i,const char*,const char*,const char*,const char*),
|
||||
void *pUserData
|
||||
) { return 0; }
|
||||
SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3 *db,
|
||||
void(*xTrace)(void *v,const char*), void* v) { return 0; }
|
||||
SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3 *db,
|
||||
void(*xProfile)(void *v,const char*,sqlite3_uint64), void* v) { return 0; }
|
||||
SQLITE_API int sqlite3_trace_v2(
|
||||
sqlite3 *db,
|
||||
unsigned uMask,
|
||||
int(*xCallback)(unsigned,void *v,void *vv,void*),
|
||||
void *pCtx
|
||||
) { return 0; }
|
||||
SQLITE_API void sqlite3_progress_handler(sqlite3 *db, int i, int(*f)(void*), void* v) { }
|
||||
SQLITE_API int sqlite3_open(
|
||||
const char *filename,
|
||||
sqlite3 **ppDb
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_open16(
|
||||
const void *filename,
|
||||
sqlite3 **ppDb
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_open_v2(
|
||||
const char *filename,
|
||||
sqlite3 **ppDb,
|
||||
int flags,
|
||||
const char *zVfs
|
||||
) { return 0; }
|
||||
SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam) { return 0; }
|
||||
SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault) { return 0; }
|
||||
SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char* s, const char* st, sqlite3_int64 i) { return 0; }
|
||||
SQLITE_API int sqlite3_errcode(sqlite3 *db) { return 0; }
|
||||
SQLITE_API int sqlite3_extended_errcode(sqlite3 *db) { return 0; }
|
||||
SQLITE_API const char *sqlite3_errmsg(sqlite3 *db) { return 0; }
|
||||
SQLITE_API const void *sqlite3_errmsg16(sqlite3 *db) { return 0; }
|
||||
SQLITE_API const char *sqlite3_errstr(int i) { return 0; }
|
||||
SQLITE_API int sqlite3_limit(sqlite3 *db, int id, int newVal) { return 0; }
|
||||
SQLITE_API int sqlite3_prepare(
|
||||
sqlite3 *db,
|
||||
const char *zSql,
|
||||
int nByte,
|
||||
sqlite3_stmt **ppStmt,
|
||||
const char **pzTail
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_prepare_v2(
|
||||
sqlite3 *db,
|
||||
const char *zSql,
|
||||
int nByte,
|
||||
sqlite3_stmt **ppStmt,
|
||||
const char **pzTail
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_prepare16(
|
||||
sqlite3 *db,
|
||||
const void *zSql,
|
||||
int nByte,
|
||||
sqlite3_stmt **ppStmt,
|
||||
const void **pzTail
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_prepare16_v2(
|
||||
sqlite3 *db,
|
||||
const void *zSql,
|
||||
int nByte,
|
||||
sqlite3_stmt **ppStmt,
|
||||
const void **pzTail
|
||||
) { return 0; }
|
||||
SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt) { return 0; }
|
||||
SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt) { return 0; }
|
||||
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt) { return 0; }
|
||||
SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt* s) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_blob(sqlite3_stmt* s, int i, const void*v, int n, void(*f)(void*)) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt* s, int i, const void *v, sqlite3_uint64 ii,
|
||||
void(*f)(void*)) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_double(sqlite3_stmt* s, int i, double d) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_int(sqlite3_stmt* s, int i, int ii) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt* s, int i, sqlite3_int64 ii) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_null(sqlite3_stmt* s, int i) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_text(sqlite3_stmt* s,int i,const char* str, int ii, void(*f)(void*)) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_text16(sqlite3_stmt* s, int i, const void *v, int ii, void(*f)(void*)) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_text64(sqlite3_stmt* s, int i, const char* str, sqlite3_uint64 ii,
|
||||
void(*f)(void*), unsigned char encoding) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_value(sqlite3_stmt* s, int i, const sqlite3_value* v) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt* s, int i, int n) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt* s, int i, sqlite3_uint64 ii) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt* s) { return 0; }
|
||||
SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt* s, int i) { return 0; }
|
||||
SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt* s, const char *zName) { return 0; }
|
||||
SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt* s) { return 0; }
|
||||
SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt) { return 0; }
|
||||
SQLITE_API const char *sqlite3_column_name(sqlite3_stmt* s, int N) { return 0; }
|
||||
SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt* s, int N) { return 0; }
|
||||
SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt* s,int i) { return 0; }
|
||||
SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt* s,int i){ return 0; }
|
||||
SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt* s,int i) { return 0; }
|
||||
SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt* s,int i) { return 0; }
|
||||
SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt* s,int i) { return 0; }
|
||||
SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt* s,int i) { return 0; }
|
||||
SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt* s,int i) { return 0; }
|
||||
SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt* s,int i) { return 0; }
|
||||
SQLITE_API int sqlite3_step(sqlite3_stmt* s) { return 0; }
|
||||
SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt) { return 0; }
|
||||
SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt* s, int iCol) { return 0; }
|
||||
SQLITE_API int sqlite3_column_bytes(sqlite3_stmt* s, int iCol) { return 0; }
|
||||
SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt* s, int iCol) { return 0; }
|
||||
SQLITE_API double sqlite3_column_double(sqlite3_stmt* s, int iCol) { return 0; }
|
||||
SQLITE_API int sqlite3_column_int(sqlite3_stmt* s, int iCol) { return 0; }
|
||||
SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt* s, int iCol) { return 0; }
|
||||
SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt* s, int iCol) { return 0; }
|
||||
SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt* s, int iCol) { return 0; }
|
||||
SQLITE_API int sqlite3_column_type(sqlite3_stmt* s, int iCol) { return 0; }
|
||||
SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt* s, int iCol) { return 0; }
|
||||
SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt) { return 0; }
|
||||
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt) { return 0; }
|
||||
SQLITE_API int sqlite3_create_function(
|
||||
sqlite3 *db,
|
||||
const char *zFunctionName,
|
||||
int nArg,
|
||||
int eTextRep,
|
||||
void *pApp,
|
||||
void (*xFunc)(sqlite3_context* c,int i,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context* c,int i,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context* c)
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_create_function16(
|
||||
sqlite3 *db,
|
||||
const void *zFunctionName,
|
||||
int nArg,
|
||||
int eTextRep,
|
||||
void *pApp,
|
||||
void (*xFunc)(sqlite3_context* c,int i,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context* c,int i,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context* c)
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_create_function_v2(
|
||||
sqlite3 *db,
|
||||
const char *zFunctionName,
|
||||
int nArg,
|
||||
int eTextRep,
|
||||
void *pApp,
|
||||
void (*xFunc)(sqlite3_context* c,int i,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context* c,int i,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context* c),
|
||||
void(*xDestroy)(void*)
|
||||
) { return 0; }
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context* c) { return 0; }
|
||||
SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt* s) { return 0; }
|
||||
SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt* s, sqlite3_stmt* ss) { return 0; }
|
||||
SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void) { return 0; }
|
||||
SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void) { }
|
||||
SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*f)(void *v,sqlite3_int64,int),
|
||||
void *v,sqlite3_int64 i) { return 0; }
|
||||
#endif
|
||||
SQLITE_API const void *sqlite3_value_blob(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API int sqlite3_value_bytes(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API int sqlite3_value_bytes16(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API double sqlite3_value_double(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API int sqlite3_value_int(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API const void *sqlite3_value_text16(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API const void *sqlite3_value_text16le(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API const void *sqlite3_value_text16be(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API int sqlite3_value_type(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value* v) { return 0; }
|
||||
SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value* v) { return 0; }
|
||||
SQLITE_API void sqlite3_value_free(sqlite3_value* v) { }
|
||||
SQLITE_API void *sqlite3_aggregate_context(sqlite3_context* c, int nBytes) { return 0; }
|
||||
SQLITE_API void *sqlite3_user_data(sqlite3_context* c) { return 0; }
|
||||
SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context* c) { return 0; }
|
||||
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context* c, int N) { return 0; }
|
||||
SQLITE_API void sqlite3_set_auxdata(sqlite3_context* c, int N, void *v, void (*f)(void*)) { }
|
||||
SQLITE_API void sqlite3_result_blob(sqlite3_context* c, const void *v, int i, void(*f)(void*)) { }
|
||||
SQLITE_API void sqlite3_result_blob64(sqlite3_context* c,const void *v,
|
||||
sqlite3_uint64 i,void(*f)(void*)) { }
|
||||
SQLITE_API void sqlite3_result_double(sqlite3_context* c, double d) { }
|
||||
SQLITE_API void sqlite3_result_error(sqlite3_context* c, const char* s, int i) { }
|
||||
SQLITE_API void sqlite3_result_error16(sqlite3_context* c, const void *v, int i) { }
|
||||
SQLITE_API void sqlite3_result_error_toobig(sqlite3_context* c) { }
|
||||
SQLITE_API void sqlite3_result_error_nomem(sqlite3_context* c) { }
|
||||
SQLITE_API void sqlite3_result_error_code(sqlite3_context* c, int i) { }
|
||||
SQLITE_API void sqlite3_result_int(sqlite3_context* c, int i) { }
|
||||
SQLITE_API void sqlite3_result_int64(sqlite3_context* c, sqlite3_int64 i) { }
|
||||
SQLITE_API void sqlite3_result_null(sqlite3_context* c) { }
|
||||
SQLITE_API void sqlite3_result_text(sqlite3_context* c, const char* s, int i, void(*f)(void*)) { }
|
||||
SQLITE_API void sqlite3_result_text64(sqlite3_context* c, const char* s, sqlite3_uint64 i,
|
||||
void(*f)(void*), unsigned char encoding) { }
|
||||
SQLITE_API void sqlite3_result_text16(sqlite3_context* c, const void *v, int i, void(*f)(void*)) { }
|
||||
SQLITE_API void sqlite3_result_text16le(sqlite3_context* c, const void *v, int i,void(*f)(void*)) { }
|
||||
SQLITE_API void sqlite3_result_text16be(sqlite3_context* c, const void *v, int i,void(*f)(void*)) { }
|
||||
SQLITE_API void sqlite3_result_value(sqlite3_context* c, sqlite3_value* v) { }
|
||||
SQLITE_API void sqlite3_result_zeroblob(sqlite3_context* c, int n) { }
|
||||
SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context* c, sqlite3_uint64 n) { return 0; }
|
||||
SQLITE_API void sqlite3_result_subtype(sqlite3_context* c,unsigned int i) { }
|
||||
SQLITE_API int sqlite3_create_collation(
|
||||
sqlite3 *db,
|
||||
const char *zName,
|
||||
int eTextRep,
|
||||
void *pArg,
|
||||
int(*xCompare)(void *,int,const void *,int,const void*)
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_create_collation_v2(
|
||||
sqlite3 *db,
|
||||
const char *zName,
|
||||
int eTextRep,
|
||||
void *pArg,
|
||||
int(*xCompare)(void *,int,const void*,int,const void*),
|
||||
void(*xDestroy)(void*)
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_create_collation16(
|
||||
sqlite3 *db,
|
||||
const void *zName,
|
||||
int eTextRep,
|
||||
void *pArg,
|
||||
int(*xCompare)(void *,int,const void *,int,const void*)
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_collation_needed(
|
||||
sqlite3 *db,
|
||||
void *v,
|
||||
void(*f)(void *v,sqlite3 *db,int eTextRep,const char*)
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_collation_needed16(
|
||||
sqlite3 *db,
|
||||
void *v,
|
||||
void(*f)(void *v,sqlite3 *db,int eTextRep,const void*)
|
||||
) { return 0; }
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
SQLITE_API int sqlite3_key(
|
||||
sqlite3 *db,
|
||||
const void *pKey, int nKey
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_key_v2(
|
||||
sqlite3 *db,
|
||||
const char *zDbName,
|
||||
const void *pKey, int nKey
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_rekey(
|
||||
sqlite3 *db,
|
||||
const void *pKey, int nKey
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_rekey_v2(
|
||||
sqlite3 *db,
|
||||
const char *zDbName,
|
||||
const void *pKey, int nKey
|
||||
) { return 0; }
|
||||
SQLITE_API void sqlite3_activate_see(
|
||||
const char *zPassPhrase
|
||||
) { return 0; }
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_CEROD
|
||||
SQLITE_API void sqlite3_activate_cerod(
|
||||
const char *zPassPhrase
|
||||
) { return 0; }
|
||||
#endif
|
||||
SQLITE_API int sqlite3_sleep(int i) { return 0; }
|
||||
SQLITE_API int sqlite3_get_autocommit(sqlite3 *db) { return 0; }
|
||||
SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt *s) { return 0; }
|
||||
SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName) { return 0; }
|
||||
SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName) { return 0; }
|
||||
SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt) { return 0; }
|
||||
SQLITE_API void *sqlite3_commit_hook(sqlite3 *db, int(*f)(void*), void *v) { return 0; }
|
||||
SQLITE_API void *sqlite3_rollback_hook(sqlite3 *db, void(*f)(void *), void *v) { return 0; }
|
||||
SQLITE_API void *sqlite3_update_hook(
|
||||
sqlite3 *db,
|
||||
void(*f)(void *,int ,char const *,char const *,sqlite3_int64),
|
||||
void *v
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_enable_shared_cache(int i) { return 0; }
|
||||
SQLITE_API int sqlite3_release_memory(int i) { return 0; }
|
||||
SQLITE_API int sqlite3_db_release_memory(sqlite3 *db) { return 0; }
|
||||
SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N) { return 0; }
|
||||
SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N) { }
|
||||
SQLITE_API int sqlite3_table_column_metadata(
|
||||
sqlite3 *db,
|
||||
const char *zDbName,
|
||||
const char *zTableName,
|
||||
const char *zColumnName,
|
||||
char const **pzDataType,
|
||||
char const **pzCollSeq,
|
||||
int *pNotNull,
|
||||
int *pPrimaryKey,
|
||||
int *pAutoinc
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_load_extension(
|
||||
sqlite3 *db,
|
||||
const char *zFile,
|
||||
const char *zProc,
|
||||
char **pzErrMsg
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff) { return 0; }
|
||||
SQLITE_API int sqlite3_auto_extension(void(*xEntryPoint)(void)) { return 0; }
|
||||
SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void)) { return 0; }
|
||||
SQLITE_API void sqlite3_reset_auto_extension(void) { }
|
||||
SQLITE_API int sqlite3_create_module(
|
||||
sqlite3 *db,
|
||||
const char *zName,
|
||||
const sqlite3_module *p,
|
||||
void *pClientData
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_create_module_v2(
|
||||
sqlite3 *db,
|
||||
const char *zName,
|
||||
const sqlite3_module *p,
|
||||
void *pClientData,
|
||||
void(*xDestroy)(void*)
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zSQL) { return 0; }
|
||||
SQLITE_API int sqlite3_overload_function(sqlite3 *db, const char *zFuncName, int nArg) { return 0; }
|
||||
SQLITE_API int sqlite3_blob_open(
|
||||
sqlite3 *db,
|
||||
const char *zDb,
|
||||
const char *zTable,
|
||||
const char *zColumn,
|
||||
sqlite3_int64 iRow,
|
||||
int flags,
|
||||
sqlite3_blob **ppBlob
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *b, sqlite3_int64 i) { return 0; }
|
||||
SQLITE_API int sqlite3_blob_close(sqlite3_blob *b) { return 0; }
|
||||
SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *b) { return 0; }
|
||||
SQLITE_API int sqlite3_blob_read(sqlite3_blob *b, void *Z, int N, int iOffset) { return 0; }
|
||||
SQLITE_API int sqlite3_blob_write(sqlite3_blob *b, const void *z, int n, int iOffset) { return 0; }
|
||||
SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName) { return 0; }
|
||||
SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *v, int makeDflt) { return 0; }
|
||||
SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs *v) { return 0; }
|
||||
SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int i) { return 0; }
|
||||
SQLITE_API void sqlite3_mutex_free(sqlite3_mutex *m) { }
|
||||
SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex *m) { }
|
||||
SQLITE_API int sqlite3_mutex_try(sqlite3_mutex *m) { return 0; }
|
||||
SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *m) { }
|
||||
#ifndef NDEBUG
|
||||
SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *m) { return 0; }
|
||||
SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *m) { return 0; }
|
||||
#endif
|
||||
SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3* db) { return 0; }
|
||||
SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void* v) { return 0; }
|
||||
SQLITE_API int sqlite3_test_control(int op, ...) { return 0; }
|
||||
SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag) { return 0; }
|
||||
SQLITE_API int sqlite3_status64(
|
||||
int op,
|
||||
sqlite3_int64 *pCurrent,
|
||||
sqlite3_int64 *pHighwater,
|
||||
int resetFlag
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_db_status(sqlite3 *db, int op, int *pCur, int *pHiwtr, int resetFlg) { return 0; }
|
||||
SQLITE_API int sqlite3_stmt_status(sqlite3_stmt* s, int op,int resetFlg) { return 0; }
|
||||
SQLITE_API sqlite3_backup *sqlite3_backup_init(
|
||||
sqlite3 *pDest,
|
||||
const char *zDestName,
|
||||
sqlite3 *pSource,
|
||||
const char *zSourceName
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage) { return 0; }
|
||||
SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p) { return 0; }
|
||||
SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p) { return 0; }
|
||||
SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p) { return 0; }
|
||||
SQLITE_API int sqlite3_unlock_notify(
|
||||
sqlite3 *pBlocked,
|
||||
void (*xNotify)(void **apArg, int nArg),
|
||||
void *pNotifyArg
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_stricmp(const char *s, const char *ss) { return 0; }
|
||||
SQLITE_API int sqlite3_strnicmp(const char *s, const char *ss, int i) { return 0; }
|
||||
SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr) { return 0; }
|
||||
SQLITE_API int sqlite3_strlike(const char *zGlob, const char *zStr, unsigned int cEsc) { return 0; }
|
||||
SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...) { }
|
||||
SQLITE_API void *sqlite3_wal_hook(
|
||||
sqlite3 *db,
|
||||
int(*f)(void *,sqlite3 *db,const char*,int i),
|
||||
void *v
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N) { return 0; }
|
||||
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb) { return 0; }
|
||||
SQLITE_API int sqlite3_wal_checkpoint_v2(
|
||||
sqlite3 *db,
|
||||
const char *zDb,
|
||||
int eMode,
|
||||
int *pnLog,
|
||||
int *pnCkpt
|
||||
) { return 0; }
|
||||
#define SQLITE_CHECKPOINT_PASSIVE 0
|
||||
#define SQLITE_CHECKPOINT_FULL 1
|
||||
#define SQLITE_CHECKPOINT_RESTART 2
|
||||
#define SQLITE_CHECKPOINT_TRUNCATE 3
|
||||
SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...) { return 0; }
|
||||
#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
|
||||
SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db) { return 0; }
|
||||
#define SQLITE_ROLLBACK 1
|
||||
#define SQLITE_FAIL 3
|
||||
#define SQLITE_REPLACE 5
|
||||
#define SQLITE_SCANSTAT_NLOOP 0
|
||||
#define SQLITE_SCANSTAT_NVISIT 1
|
||||
#define SQLITE_SCANSTAT_EST 2
|
||||
#define SQLITE_SCANSTAT_NAME 3
|
||||
#define SQLITE_SCANSTAT_EXPLAIN 4
|
||||
#define SQLITE_SCANSTAT_SELECTID 5
|
||||
SQLITE_API int sqlite3_stmt_scanstatus(
|
||||
sqlite3_stmt *pStmt,
|
||||
int idx,
|
||||
int iScanStatusOp,
|
||||
void *pOut
|
||||
) { return 0; }
|
||||
SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *s) { }
|
||||
SQLITE_API int sqlite3_db_cacheflush(sqlite3* db) { return 0; }
|
||||
#if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
||||
SQLITE_API void *sqlite3_preupdate_hook(
|
||||
sqlite3 *db,
|
||||
void(*xPreUpdate)(
|
||||
void *pCtx,
|
||||
sqlite3 *db,
|
||||
int op,
|
||||
char const *zDb,
|
||||
char const *zName,
|
||||
sqlite3_int64 iKey1,
|
||||
sqlite3_int64 iKey2
|
||||
),
|
||||
void*
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int i, sqlite3_value **) { return 0; }
|
||||
SQLITE_API int sqlite3_preupdate_count(sqlite3 *) { return 0; }
|
||||
SQLITE_API int sqlite3_preupdate_depth(sqlite3 *) { return 0; }
|
||||
SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int i, sqlite3_value **) { return 0; }
|
||||
#endif
|
||||
SQLITE_API int sqlite3_system_errno(sqlite3 *db) { return 0; }
|
||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
|
||||
sqlite3 *db,
|
||||
const char *zSchema,
|
||||
sqlite3_snapshot **ppSnapshot
|
||||
) { return 0; }
|
||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
|
||||
sqlite3 *db,
|
||||
const char *zSchema,
|
||||
sqlite3_snapshot *pSnapshot
|
||||
) { return 0; }
|
||||
SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot* s) { }
|
||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
|
||||
sqlite3_snapshot *p1,
|
||||
sqlite3_snapshot *p2
|
||||
) { return 0; }
|
||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb) { return 0; }
|
||||
SQLITE_API int sqlite3_rtree_geometry_callback(
|
||||
sqlite3 *db,
|
||||
const char *zGeom,
|
||||
int (*xGeom)(sqlite3_rtree_geometry*, int i, sqlite3_rtree_dbl*,int*),
|
||||
void *pContext
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3_rtree_query_callback(
|
||||
sqlite3 *db,
|
||||
const char *zQueryFunc,
|
||||
int (*xQueryFunc)(sqlite3_rtree_query_info*),
|
||||
void *pContext,
|
||||
void (*xDestructor)(void*)
|
||||
) { return 0; }
|
||||
typedef struct sqlite3_session sqlite3_session;
|
||||
typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
|
||||
SQLITE_API int sqlite3session_create(
|
||||
sqlite3 *db,
|
||||
const char *zDb,
|
||||
sqlite3_session **ppSession
|
||||
) { return 0; }
|
||||
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession) { }
|
||||
SQLITE_API int sqlite3session_enable(sqlite3_session *pSession, int bEnable) { return 0; }
|
||||
SQLITE_API int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect) { return 0; }
|
||||
SQLITE_API int sqlite3session_attach(
|
||||
sqlite3_session *pSession,
|
||||
const char *zTab
|
||||
) { return 0; }
|
||||
SQLITE_API void sqlite3session_table_filter(
|
||||
sqlite3_session *pSession,
|
||||
int(*xFilter)(
|
||||
void *pCtx,
|
||||
const char *zTab
|
||||
),
|
||||
void *pCtx
|
||||
) { }
|
||||
SQLITE_API int sqlite3session_changeset(
|
||||
sqlite3_session *pSession,
|
||||
int *pnChangeset,
|
||||
void **ppChangeset
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3session_diff(
|
||||
sqlite3_session *pSession,
|
||||
const char *zFromDb,
|
||||
const char *zTbl,
|
||||
char **pzErrMsg
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3session_patchset(
|
||||
sqlite3_session *pSession,
|
||||
int *pnPatchset,
|
||||
void **ppPatchset
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_start(
|
||||
sqlite3_changeset_iter **pp,
|
||||
int nChangeset,
|
||||
void *pChangeset
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_op(
|
||||
sqlite3_changeset_iter *pIter,
|
||||
const char **pzTab,
|
||||
int *pnCol,
|
||||
int *pOp,
|
||||
int *pbIndirect
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_pk(
|
||||
sqlite3_changeset_iter *pIter,
|
||||
unsigned char **pabPK,
|
||||
int *pnCol
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_old(
|
||||
sqlite3_changeset_iter *pIter,
|
||||
int iVal,
|
||||
sqlite3_value **ppValue
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_new(
|
||||
sqlite3_changeset_iter *pIter,
|
||||
int iVal,
|
||||
sqlite3_value **ppValue
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_conflict(
|
||||
sqlite3_changeset_iter *pIter,
|
||||
int iVal,
|
||||
sqlite3_value **ppValue
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_fk_conflicts(
|
||||
sqlite3_changeset_iter *pIter,
|
||||
int *pnOut
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_invert(
|
||||
int nIn, const void *pIn,
|
||||
int *pnOut, void **ppOut
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_concat(
|
||||
int nA,
|
||||
void *pA,
|
||||
int nB,
|
||||
void *pB,
|
||||
int *pnOut,
|
||||
void **ppOut
|
||||
) { return 0; }
|
||||
typedef struct sqlite3_changegroup sqlite3_changegroup;
|
||||
int sqlite3changegroup_new(sqlite3_changegroup **pp) { return 0; }
|
||||
int sqlite3changegroup_add(sqlite3_changegroup *c, int nData, void *pData) { return 0; }
|
||||
int sqlite3changegroup_output(
|
||||
sqlite3_changegroup *c,
|
||||
int *pnData,
|
||||
void **ppData
|
||||
) { return 0; }
|
||||
void sqlite3changegroup_delete(sqlite3_changegroup *c) { }
|
||||
SQLITE_API int sqlite3changeset_apply(
|
||||
sqlite3 *db,
|
||||
int nChangeset,
|
||||
void *pChangeset,
|
||||
int(*xFilter)(
|
||||
void *pCtx,
|
||||
const char *zTab
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx,
|
||||
int eConflict,
|
||||
sqlite3_changeset_iter *p
|
||||
),
|
||||
void *pCtx
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_apply_strm(
|
||||
sqlite3 *db,
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData),
|
||||
void *pIn,
|
||||
int(*xFilter)(
|
||||
void *pCtx,
|
||||
const char *zTab
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx,
|
||||
int eConflict,
|
||||
sqlite3_changeset_iter *p
|
||||
),
|
||||
void *pCtx
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_concat_strm(
|
||||
int (*xInputA)(void *pIn, void *pData, int *pnData),
|
||||
void *pInA,
|
||||
int (*xInputB)(void *pIn, void *pData, int *pnData),
|
||||
void *pInB,
|
||||
int (*xOutput)(void *pOut, const void *pData, int nData),
|
||||
void *pOut
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_invert_strm(
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData),
|
||||
void *pIn,
|
||||
int (*xOutput)(void *pOut, const void *pData, int nData),
|
||||
void *pOut
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3changeset_start_strm(
|
||||
sqlite3_changeset_iter **pp,
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData),
|
||||
void *pIn
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3session_changeset_strm(
|
||||
sqlite3_session *pSession,
|
||||
int (*xOutput)(void *pOut, const void *pData, int nData),
|
||||
void *pOut
|
||||
) { return 0; }
|
||||
SQLITE_API int sqlite3session_patchset_strm(
|
||||
sqlite3_session *pSession,
|
||||
int (*xOutput)(void *pOut, const void *pData, int nData),
|
||||
void *pOut
|
||||
) { return 0; }
|
||||
int sqlite3changegroup_add_strm(sqlite3_changegroup *c,
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData),
|
||||
void *pIn
|
||||
) { return 0; }
|
||||
int sqlite3changegroup_output_strm(sqlite3_changegroup *c,
|
||||
int (*xOutput)(void *pOut, const void *pData, int nData),
|
||||
void *pOut
|
||||
) { return 0; }
|
||||
92
jni/magisk.h
Normal file
92
jni/magisk.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* magisk.h - Top header
|
||||
*/
|
||||
|
||||
#ifndef _MAGISK_H_
|
||||
#define _MAGISK_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#define MAGISK_VER_STR xstr(MAGISK_VERSION) ":MAGISK"
|
||||
|
||||
#define str(a) #a
|
||||
#define xstr(a) str(a)
|
||||
|
||||
#define REQUESTOR_DAEMON_PATH "\0MAGISK"
|
||||
#define REQUESTOR_DAEMON_PATH_LEN 7
|
||||
|
||||
#define LOG_TAG "Magisk"
|
||||
|
||||
#ifndef ARG_MAX
|
||||
#define ARG_MAX 4096
|
||||
#endif
|
||||
|
||||
#define LOGFILE "/cache/magisk.log"
|
||||
#define LASTLOG "/cache/last_magisk.log"
|
||||
#define DEBUG_LOG "/data/magisk_debug.log"
|
||||
#define UNBLOCKFILE "/dev/.magisk.unblock"
|
||||
#define DISABLEFILE "/cache/.disable_magisk"
|
||||
#define UNINSTALLER "/cache/magisk_uninstaller.sh"
|
||||
#define MOUNTPOINT "/magisk"
|
||||
#define COREDIR MOUNTPOINT "/.core"
|
||||
#define HOSTSFILE COREDIR "/hosts"
|
||||
#define HIDELIST COREDIR "/hidelist"
|
||||
#define MAINIMG "/data/magisk.img"
|
||||
#define DATABIN "/data/magisk"
|
||||
#define MANAGERAPK DATABIN "/magisk.apk"
|
||||
#define MAGISKTMP "/dev/magisk"
|
||||
#define MIRRDIR MAGISKTMP "/mirror"
|
||||
#define DUMMDIR MAGISKTMP "/dummy"
|
||||
#define CACHEMOUNT "/cache/magisk_mount"
|
||||
|
||||
#define SELINUX_PATH "/sys/fs/selinux/"
|
||||
#define SELINUX_ENFORCE SELINUX_PATH "enforce"
|
||||
#define SELINUX_POLICY SELINUX_PATH "policy"
|
||||
#define SELINUX_LOAD SELINUX_PATH "load"
|
||||
|
||||
#define MAGISKHIDE_PROP "persist.magisk.hide"
|
||||
|
||||
// Global handler for PLOGE
|
||||
extern __thread void (*err_handler)(void);
|
||||
|
||||
// Common error handlers
|
||||
static inline void exit_proc() { exit(1); }
|
||||
static inline void exit_thread() { pthread_exit(NULL); }
|
||||
static inline void do_nothing() {}
|
||||
|
||||
// Dummy function to depress debug message
|
||||
static inline void stub(const char *fmt, ...) {}
|
||||
|
||||
#ifdef DEBUG
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define LOGD(...) stub(__VA_ARGS__)
|
||||
#endif
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
#define PLOGE(fmt, args...) { LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)); err_handler(); }
|
||||
|
||||
extern char *argv0; /* For changing process name */
|
||||
|
||||
extern char *applet[];
|
||||
extern int (*applet_main[]) (int, char *[]);
|
||||
|
||||
// Multi-call entrypoints
|
||||
int magiskhide_main(int argc, char *argv[]);
|
||||
int magiskpolicy_main(int argc, char *argv[]);
|
||||
int su_client_main(int argc, char *argv[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int resetprop_main(int argc, char *argv[]);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
30
jni/magiskboot/Android.mk
Normal file
30
jni/magiskboot/Android.mk
Normal file
@@ -0,0 +1,30 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magiskboot
|
||||
LOCAL_STATIC_LIBRARIES := libz liblzma liblz4 libbz2
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/utils \
|
||||
jni/ndk-compression/zlib/ \
|
||||
jni/ndk-compression/xz/src/liblzma/api/ \
|
||||
jni/ndk-compression/lz4/lib/ \
|
||||
jni/ndk-compression/bzip2/
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
main.c \
|
||||
bootimg.c \
|
||||
hexpatch.c \
|
||||
compress.c \
|
||||
boot_utils.c \
|
||||
cpio.c \
|
||||
sha1.c \
|
||||
../utils/xwrap.c \
|
||||
../utils/vector.c \
|
||||
../utils/list.c
|
||||
LOCAL_CFLAGS += -DZLIB_CONST
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include jni/ndk-compression/zlib/Android.mk
|
||||
include jni/ndk-compression/xz/src/liblzma/Android.mk
|
||||
include jni/ndk-compression/lz4/lib/Android.mk
|
||||
include jni/ndk-compression/bzip2/Android.mk
|
||||
97
jni/magiskboot/boot_utils.c
Normal file
97
jni/magiskboot/boot_utils.c
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "magiskboot.h"
|
||||
|
||||
char *SUP_LIST[] = { "gzip", "xz", "lzma", "bzip2", "lz4", "lz4_legacy", NULL };
|
||||
char *SUP_EXT_LIST[] = { "gz", "xz", "lzma", "bz2", "lz4", "lz4", NULL };
|
||||
file_t SUP_TYPE_LIST[] = { GZIP, XZ, LZMA, BZIP2, LZ4, LZ4_LEGACY, 0 };
|
||||
|
||||
void mmap_ro(const char *filename, unsigned char **buf, size_t *size) {
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
*size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
*buf = xmmap(NULL, *size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void mmap_rw(const char *filename, unsigned char **buf, size_t *size) {
|
||||
int fd = xopen(filename, O_RDWR);
|
||||
*size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
*buf = xmmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
file_t check_type(const unsigned char *buf) {
|
||||
if (memcmp(buf, CHROMEOS_MAGIC, 8) == 0) {
|
||||
return CHROMEOS;
|
||||
} else if (memcmp(buf, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) {
|
||||
return AOSP;
|
||||
} else if (memcmp(buf, ELF32_MAGIC, 5) == 0) {
|
||||
return ELF32;
|
||||
} else if (memcmp(buf, ELF64_MAGIC, 5) == 0) {
|
||||
return ELF64;
|
||||
} else if (memcmp(buf, "\x1f\x8b\x08\x00", 4) == 0) {
|
||||
return GZIP;
|
||||
} else if (memcmp(buf, "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a", 9) == 0) {
|
||||
return LZOP;
|
||||
} else if (memcmp(buf, "\xfd""7zXZ\x00", 6) == 0) {
|
||||
return XZ;
|
||||
} else if (memcmp(buf, "\x5d\x00\x00", 3) == 0
|
||||
&& (buf[12] == (unsigned char) '\xff' || buf[12] == (unsigned char) '\x00')) {
|
||||
return LZMA;
|
||||
} else if (memcmp(buf, "BZh", 3) == 0) {
|
||||
return BZIP2;
|
||||
} else if (memcmp(buf, "\x04\x22\x4d\x18", 4) == 0) {
|
||||
return LZ4;
|
||||
} else if (memcmp(buf, "\x02\x21\x4c\x18", 4) == 0) {
|
||||
return LZ4_LEGACY;
|
||||
} else if (memcmp(buf, "\x88\x16\x88\x58", 4) == 0) {
|
||||
return MTK;
|
||||
} else {
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
void write_zero(int fd, size_t size) {
|
||||
size_t pos = lseek(fd, 0, SEEK_CUR);
|
||||
ftruncate(fd, pos + size);
|
||||
lseek(fd, pos + size, SEEK_SET);
|
||||
}
|
||||
|
||||
void mem_align(size_t *pos, size_t align) {
|
||||
size_t mask = align - 1;
|
||||
if (*pos & mask) {
|
||||
*pos += align - (*pos & mask);
|
||||
}
|
||||
}
|
||||
|
||||
void file_align(int fd, size_t align, int out) {
|
||||
size_t pos = lseek(fd, 0, SEEK_CUR);
|
||||
size_t mask = align - 1;
|
||||
size_t off;
|
||||
if (pos & mask) {
|
||||
off = align - (pos & mask);
|
||||
if (out) {
|
||||
write_zero(fd, off);
|
||||
} else {
|
||||
lseek(fd, pos + off, SEEK_SET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int open_new(const char *filename) {
|
||||
return xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
fprintf(stderr, "Cleaning up...\n");
|
||||
char name[PATH_MAX];
|
||||
unlink(KERNEL_FILE);
|
||||
unlink(RAMDISK_FILE);
|
||||
unlink(RAMDISK_FILE ".unsupport");
|
||||
unlink(SECOND_FILE);
|
||||
unlink(DTB_FILE);
|
||||
for (int i = 0; SUP_EXT_LIST[i]; ++i) {
|
||||
sprintf(name, "%s.%s", RAMDISK_FILE, SUP_EXT_LIST[i]);
|
||||
unlink(name);
|
||||
}
|
||||
}
|
||||
310
jni/magiskboot/bootimg.c
Normal file
310
jni/magiskboot/bootimg.c
Normal file
@@ -0,0 +1,310 @@
|
||||
#include "bootimg.h"
|
||||
#include "magiskboot.h"
|
||||
|
||||
static unsigned char *kernel, *ramdisk, *second, *dtb, *extra;
|
||||
static boot_img_hdr hdr;
|
||||
static int mtk_kernel = 0, mtk_ramdisk = 0;
|
||||
static file_t ramdisk_type;
|
||||
|
||||
static void dump(unsigned char *buf, size_t size, const char *filename) {
|
||||
int fd = open_new(filename);
|
||||
xwrite(fd, buf, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static size_t restore(const char *filename, int fd) {
|
||||
int ifd = xopen(filename, O_RDONLY);
|
||||
size_t size = lseek(ifd, 0, SEEK_END);
|
||||
lseek(ifd, 0, SEEK_SET);
|
||||
xsendfile(fd, ifd, NULL, size);
|
||||
close(ifd);
|
||||
return size;
|
||||
}
|
||||
|
||||
static void restore_buf(int fd, const void *buf, size_t size) {
|
||||
xwrite(fd, buf, size);
|
||||
}
|
||||
|
||||
static void print_info() {
|
||||
fprintf(stderr, "KERNEL [%d] @ 0x%08x\n", hdr.kernel_size, hdr.kernel_addr);
|
||||
fprintf(stderr, "RAMDISK [%d] @ 0x%08x\n", hdr.ramdisk_size, hdr.ramdisk_addr);
|
||||
fprintf(stderr, "SECOND [%d] @ 0x%08x\n", hdr.second_size, hdr.second_addr);
|
||||
fprintf(stderr, "DTB [%d] @ 0x%08x\n", hdr.dt_size, hdr.tags_addr);
|
||||
fprintf(stderr, "PAGESIZE [%d]\n", hdr.page_size);
|
||||
if (hdr.os_version != 0) {
|
||||
int a,b,c,y,m = 0;
|
||||
int os_version, os_patch_level;
|
||||
os_version = hdr.os_version >> 11;
|
||||
os_patch_level = hdr.os_version & 0x7ff;
|
||||
|
||||
a = (os_version >> 14) & 0x7f;
|
||||
b = (os_version >> 7) & 0x7f;
|
||||
c = os_version & 0x7f;
|
||||
fprintf(stderr, "OS_VERSION [%d.%d.%d]\n", a, b, c);
|
||||
|
||||
y = (os_patch_level >> 4) + 2000;
|
||||
m = os_patch_level & 0xf;
|
||||
fprintf(stderr, "PATCH_LEVEL [%d-%02d]\n", y, m);
|
||||
}
|
||||
fprintf(stderr, "NAME [%s]\n", hdr.name);
|
||||
fprintf(stderr, "CMDLINE [%s]\n", hdr.cmdline);
|
||||
|
||||
switch (ramdisk_type) {
|
||||
case GZIP:
|
||||
fprintf(stderr, "COMPRESSION [%s]\n", "gzip");
|
||||
break;
|
||||
case XZ:
|
||||
fprintf(stderr, "COMPRESSION [%s]\n", "xz");
|
||||
break;
|
||||
case LZMA:
|
||||
fprintf(stderr, "COMPRESSION [%s]\n", "lzma");
|
||||
break;
|
||||
case BZIP2:
|
||||
fprintf(stderr, "COMPRESSION [%s]\n", "bzip2");
|
||||
break;
|
||||
case LZ4:
|
||||
fprintf(stderr, "COMPRESSION [%s]\n", "lz4");
|
||||
break;
|
||||
case LZ4_LEGACY:
|
||||
fprintf(stderr, "COMPRESSION [%s]\n", "lz4_legacy");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown ramdisk format!\n");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
int parse_img(unsigned char *orig, size_t size) {
|
||||
unsigned char *base, *end;
|
||||
size_t pos = 0;
|
||||
int ret = 0;
|
||||
for(base = orig, end = orig + size; base < end; base += 256, size -= 256) {
|
||||
switch (check_type(base)) {
|
||||
case CHROMEOS:
|
||||
// The caller should know it's chromeos, as it needs additional signing
|
||||
ret = 2;
|
||||
continue;
|
||||
case ELF32:
|
||||
exit(3);
|
||||
case ELF64:
|
||||
exit(4);
|
||||
case AOSP:
|
||||
// Read the header
|
||||
memcpy(&hdr, base, sizeof(hdr));
|
||||
pos += hdr.page_size;
|
||||
|
||||
// Kernel position
|
||||
kernel = base + pos;
|
||||
pos += hdr.kernel_size;
|
||||
mem_align(&pos, hdr.page_size);
|
||||
|
||||
// Ramdisk position
|
||||
ramdisk = base + pos;
|
||||
pos += hdr.ramdisk_size;
|
||||
mem_align(&pos, hdr.page_size);
|
||||
|
||||
if (hdr.second_size) {
|
||||
// Second position
|
||||
second = base + pos;
|
||||
pos += hdr.second_size;
|
||||
mem_align(&pos, hdr.page_size);
|
||||
}
|
||||
|
||||
if (hdr.dt_size) {
|
||||
// dtb position
|
||||
dtb = base + pos;
|
||||
pos += hdr.dt_size;
|
||||
mem_align(&pos, hdr.page_size);
|
||||
}
|
||||
|
||||
if (pos < size) {
|
||||
extra = base + pos;
|
||||
}
|
||||
|
||||
// Check ramdisk compression type
|
||||
ramdisk_type = check_type(ramdisk);
|
||||
|
||||
// Check MTK
|
||||
if (check_type(kernel) == MTK) {
|
||||
fprintf(stderr, "MTK header found in kernel\n");
|
||||
mtk_kernel = 1;
|
||||
}
|
||||
if (ramdisk_type == MTK) {
|
||||
fprintf(stderr, "MTK header found in ramdisk\n");
|
||||
mtk_ramdisk = 1;
|
||||
ramdisk_type = check_type(ramdisk + 512);
|
||||
}
|
||||
|
||||
// Print info
|
||||
print_info();
|
||||
return ret;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
LOGE(1, "No boot image magic found!\n");
|
||||
}
|
||||
|
||||
void unpack(const char* image) {
|
||||
size_t size;
|
||||
unsigned char *orig;
|
||||
mmap_ro(image, &orig, &size);
|
||||
|
||||
// Parse image
|
||||
fprintf(stderr, "Parsing boot image: [%s]\n\n", image);
|
||||
int ret = parse_img(orig, size);
|
||||
|
||||
// Dump kernel
|
||||
if (mtk_kernel) {
|
||||
kernel += 512;
|
||||
hdr.kernel_size -= 512;
|
||||
}
|
||||
dump(kernel, hdr.kernel_size, KERNEL_FILE);
|
||||
|
||||
// Dump ramdisk
|
||||
if (mtk_ramdisk) {
|
||||
ramdisk += 512;
|
||||
hdr.ramdisk_size -= 512;
|
||||
}
|
||||
if (decomp(ramdisk_type, RAMDISK_FILE, ramdisk, hdr.ramdisk_size)) {
|
||||
// Dump the compressed ramdisk
|
||||
dump(ramdisk, hdr.ramdisk_size, RAMDISK_FILE ".unsupport");
|
||||
LOGE(1, "Unsupported ramdisk format! Dumped to %s\n", RAMDISK_FILE ".unsupport");
|
||||
}
|
||||
|
||||
if (hdr.second_size) {
|
||||
// Dump second
|
||||
dump(second, hdr.second_size, SECOND_FILE);
|
||||
}
|
||||
|
||||
if (hdr.dt_size) {
|
||||
// Dump dtb
|
||||
dump(dtb, hdr.dt_size, DTB_FILE);
|
||||
}
|
||||
|
||||
munmap(orig, size);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
void repack(const char* orig_image, const char* out_image) {
|
||||
size_t size;
|
||||
unsigned char *orig;
|
||||
char name[PATH_MAX];
|
||||
|
||||
// There are possible two MTK headers
|
||||
mtk_hdr mtk_kernel_hdr, mtk_ramdisk_hdr;
|
||||
size_t mtk_kernel_off, mtk_ramdisk_off;
|
||||
|
||||
// Load original image
|
||||
mmap_ro(orig_image, &orig, &size);
|
||||
|
||||
// Parse original image
|
||||
fprintf(stderr, "Parsing boot image: [%s]\n\n", orig_image);
|
||||
parse_img(orig, size);
|
||||
|
||||
fprintf(stderr, "Repack to boot image: [%s]\n\n", out_image);
|
||||
|
||||
// Create new image
|
||||
int fd = open_new(out_image);
|
||||
|
||||
// Set all sizes to 0
|
||||
hdr.kernel_size = 0;
|
||||
hdr.ramdisk_size = 0;
|
||||
hdr.second_size = 0;
|
||||
hdr.dt_size = 0;
|
||||
|
||||
// Skip a page for header
|
||||
write_zero(fd, hdr.page_size);
|
||||
|
||||
// Restore kernel
|
||||
if (mtk_kernel) {
|
||||
mtk_kernel_off = lseek(fd, 0, SEEK_CUR);
|
||||
restore_buf(fd, kernel, 512);
|
||||
memcpy(&mtk_kernel_hdr, kernel, sizeof(mtk_kernel_hdr));
|
||||
}
|
||||
hdr.kernel_size = restore(KERNEL_FILE, fd);
|
||||
file_align(fd, hdr.page_size, 1);
|
||||
|
||||
// Restore ramdisk
|
||||
if (mtk_ramdisk) {
|
||||
mtk_ramdisk_off = lseek(fd, 0, SEEK_CUR);
|
||||
restore_buf(fd, ramdisk, 512);
|
||||
memcpy(&mtk_ramdisk_hdr, ramdisk, sizeof(mtk_ramdisk_hdr));
|
||||
}
|
||||
if (access(RAMDISK_FILE, R_OK) == 0) {
|
||||
// If we found raw cpio, compress to original format
|
||||
|
||||
// Before we start, clean up previous compressed files
|
||||
for (int i = 0; SUP_EXT_LIST[i]; ++i) {
|
||||
sprintf(name, "%s.%s", RAMDISK_FILE, SUP_EXT_LIST[i]);
|
||||
unlink(name);
|
||||
}
|
||||
|
||||
size_t cpio_size;
|
||||
unsigned char *cpio;
|
||||
mmap_ro(RAMDISK_FILE, &cpio, &cpio_size);
|
||||
|
||||
if (comp(ramdisk_type, RAMDISK_FILE, cpio, cpio_size))
|
||||
LOGE(1, "Unsupported ramdisk format!\n");
|
||||
|
||||
munmap(cpio, cpio_size);
|
||||
}
|
||||
|
||||
int found = 0;
|
||||
for (int i = 0; SUP_EXT_LIST[i]; ++i) {
|
||||
sprintf(name, "%s.%s", RAMDISK_FILE, SUP_EXT_LIST[i]);
|
||||
if (access(name, R_OK) == 0) {
|
||||
ramdisk_type = SUP_TYPE_LIST[i];
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
LOGE(1, "No ramdisk exists!\n");
|
||||
hdr.ramdisk_size = restore(name, fd);
|
||||
file_align(fd, hdr.page_size, 1);
|
||||
|
||||
// Restore second
|
||||
if (access(SECOND_FILE, R_OK) == 0) {
|
||||
hdr.second_size = restore(SECOND_FILE, fd);
|
||||
file_align(fd, hdr.page_size, 1);
|
||||
}
|
||||
|
||||
// Restore dtb
|
||||
if (access(DTB_FILE, R_OK) == 0) {
|
||||
hdr.dt_size = restore(DTB_FILE, fd);
|
||||
file_align(fd, hdr.page_size, 1);
|
||||
}
|
||||
|
||||
// Check extra info, currently only for LG Bump and Samsung SEANDROIDENFORCE
|
||||
if (extra) {
|
||||
if (memcmp(extra, "SEANDROIDENFORCE", 16) == 0 ||
|
||||
memcmp(extra, "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79", 16) == 0 ) {
|
||||
restore_buf(fd, extra, 16);
|
||||
}
|
||||
}
|
||||
|
||||
// Write headers back
|
||||
if (mtk_kernel) {
|
||||
lseek(fd, mtk_kernel_off, SEEK_SET);
|
||||
mtk_kernel_hdr.size = hdr.kernel_size;
|
||||
hdr.kernel_size += 512;
|
||||
restore_buf(fd, &mtk_kernel_hdr, sizeof(mtk_kernel_hdr));
|
||||
}
|
||||
if (mtk_ramdisk) {
|
||||
lseek(fd, mtk_ramdisk_off, SEEK_SET);
|
||||
mtk_ramdisk_hdr.size = hdr.ramdisk_size;
|
||||
hdr.ramdisk_size += 512;
|
||||
restore_buf(fd, &mtk_ramdisk_hdr, sizeof(mtk_ramdisk_hdr));
|
||||
}
|
||||
// Main header
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
restore_buf(fd, &hdr, sizeof(hdr));
|
||||
|
||||
// Print new image info
|
||||
print_info();
|
||||
|
||||
munmap(orig, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,14 @@ struct boot_img_hdr
|
||||
|
||||
uint32_t tags_addr; /* physical addr for kernel tags */
|
||||
uint32_t page_size; /* flash page size we assume */
|
||||
uint32_t unused[2]; /* future expansion: should be 0 */
|
||||
uint32_t dt_size; /* device tree in bytes */
|
||||
|
||||
/* operating system version and security patch level; for
|
||||
* version "A.B.C" and patch level "Y-M-D":
|
||||
* ver = A << 14 | B << 7 | C (7 bits for each of A, B, C)
|
||||
* lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M)
|
||||
* os_version = ver << 11 | lvl */
|
||||
uint32_t os_version;
|
||||
|
||||
uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
|
||||
|
||||
@@ -66,10 +73,13 @@ struct boot_img_hdr
|
||||
** +-----------------+
|
||||
** | second stage | o pages
|
||||
** +-----------------+
|
||||
** | device tree | p pages
|
||||
** +-----------------+
|
||||
**
|
||||
** n = (kernel_size + page_size - 1) / page_size
|
||||
** m = (ramdisk_size + page_size - 1) / page_size
|
||||
** o = (second_size + page_size - 1) / page_size
|
||||
** p = (dt_size + page_size - 1) / page_size
|
||||
**
|
||||
** 0. all entities are page_size aligned in flash
|
||||
** 1. kernel and ramdisk are required (size != 0)
|
||||
@@ -83,8 +93,10 @@ struct boot_img_hdr
|
||||
** else: jump to kernel_addr
|
||||
*/
|
||||
|
||||
int extract(char *image);
|
||||
int repack(char *image);
|
||||
int hexpatch(char *image, char *from, char *to);
|
||||
typedef struct mtk_hdr {
|
||||
uint8_t magic[4]; /* MTK magic */
|
||||
uint32_t size; /* Size of the content */
|
||||
uint8_t name[32]; /* The type of the header */
|
||||
} mtk_hdr;
|
||||
|
||||
#endif
|
||||
569
jni/magiskboot/compress.c
Normal file
569
jni/magiskboot/compress.c
Normal file
@@ -0,0 +1,569 @@
|
||||
#include <zlib.h>
|
||||
#include <lzma.h>
|
||||
#include <lz4.h>
|
||||
#include <lz4frame.h>
|
||||
#include <bzlib.h>
|
||||
|
||||
#include "magiskboot.h"
|
||||
|
||||
#define windowBits 15
|
||||
#define ZLIB_GZIP 16
|
||||
#define memLevel 8
|
||||
#define CHUNK 0x40000
|
||||
|
||||
#define LZ4_HEADER_SIZE 19
|
||||
#define LZ4_FOOTER_SIZE 4
|
||||
#define LZ4_LEGACY_BLOCKSIZE 0x800000
|
||||
|
||||
static void write_file(const int fd, const void *buf, const size_t size, const char *filename) {
|
||||
xwrite(fd, buf, size);
|
||||
}
|
||||
|
||||
static void report(const int mode, const char* filename) {
|
||||
switch(mode) {
|
||||
case 0:
|
||||
fprintf(stderr, "Decompressing to [%s]\n\n", filename);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Compressing to [%s]\n\n", filename);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Mode: 0 = decode; 1 = encode
|
||||
void gzip(int mode, const char* filename, const unsigned char* buf, size_t size) {
|
||||
size_t ret = 0, flush, have, pos = 0;
|
||||
z_stream strm;
|
||||
unsigned char out[CHUNK];
|
||||
|
||||
report(mode, filename);
|
||||
int fd = open_new(filename);
|
||||
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = inflateInit2(&strm, windowBits | ZLIB_GZIP);
|
||||
break;
|
||||
case 1:
|
||||
ret = deflateInit2(&strm, 9, Z_DEFLATED, windowBits | ZLIB_GZIP, memLevel, Z_DEFAULT_STRATEGY);
|
||||
break;
|
||||
default:
|
||||
LOGE(1, "Unsupported gzip mode!\n");
|
||||
}
|
||||
|
||||
if (ret != Z_OK)
|
||||
LOGE(1, "Unable to init zlib stream\n");
|
||||
|
||||
do {
|
||||
strm.next_in = buf + pos;
|
||||
if (pos + CHUNK >= size) {
|
||||
strm.avail_in = size - pos;
|
||||
flush = Z_FINISH;
|
||||
} else {
|
||||
strm.avail_in = CHUNK;
|
||||
flush = Z_NO_FLUSH;
|
||||
}
|
||||
pos += strm.avail_in;
|
||||
|
||||
do {
|
||||
strm.avail_out = CHUNK;
|
||||
strm.next_out = out;
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = inflate(&strm, flush);
|
||||
break;
|
||||
case 1:
|
||||
ret = deflate(&strm, flush);
|
||||
break;
|
||||
}
|
||||
if (ret == Z_STREAM_ERROR)
|
||||
LOGE(1, "Error when running gzip\n");
|
||||
|
||||
have = CHUNK - strm.avail_out;
|
||||
write_file(fd, out, have, filename);
|
||||
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
} while(pos < size);
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
inflateEnd(&strm);
|
||||
break;
|
||||
case 1:
|
||||
deflateEnd(&strm);
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
||||
// Mode: 0 = decode xz/lzma; 1 = encode xz; 2 = encode lzma
|
||||
void lzma(int mode, const char* filename, const unsigned char* buf, size_t size) {
|
||||
size_t have, pos = 0;
|
||||
lzma_ret ret = 0;
|
||||
lzma_stream strm = LZMA_STREAM_INIT;
|
||||
lzma_options_lzma opt;
|
||||
lzma_action action;
|
||||
unsigned char out[BUFSIZ];
|
||||
|
||||
report(mode, filename);
|
||||
int fd = open_new(filename);
|
||||
|
||||
// Initialize preset
|
||||
lzma_lzma_preset(&opt, LZMA_PRESET_DEFAULT);
|
||||
lzma_filter filters[] = {
|
||||
{ .id = LZMA_FILTER_LZMA2, .options = &opt },
|
||||
{ .id = LZMA_VLI_UNKNOWN, .options = NULL },
|
||||
};
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = lzma_auto_decoder(&strm, UINT64_MAX, 0);
|
||||
break;
|
||||
case 1:
|
||||
ret = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC32);
|
||||
break;
|
||||
case 2:
|
||||
ret = lzma_alone_encoder(&strm, &opt);
|
||||
break;
|
||||
default:
|
||||
LOGE(1, "Unsupported lzma mode!\n");
|
||||
}
|
||||
|
||||
|
||||
if (ret != LZMA_OK)
|
||||
LOGE(1, "Unable to init lzma stream\n");
|
||||
|
||||
do {
|
||||
strm.next_in = buf + pos;
|
||||
if (pos + BUFSIZ >= size) {
|
||||
strm.avail_in = size - pos;
|
||||
action = LZMA_FINISH;
|
||||
} else {
|
||||
strm.avail_in = BUFSIZ;
|
||||
action = LZMA_RUN;
|
||||
}
|
||||
pos += strm.avail_in;
|
||||
|
||||
do {
|
||||
strm.avail_out = BUFSIZ;
|
||||
strm.next_out = out;
|
||||
ret = lzma_code(&strm, action);
|
||||
have = BUFSIZ - strm.avail_out;
|
||||
write_file(fd, out, have, filename);
|
||||
} while (strm.avail_out == 0 && ret == LZMA_OK);
|
||||
|
||||
if (ret != LZMA_OK && ret != LZMA_STREAM_END)
|
||||
LOGE(1, "LZMA error %d!\n", ret);
|
||||
|
||||
} while (pos < size);
|
||||
|
||||
lzma_end(&strm);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// Mode: 0 = decode; 1 = encode
|
||||
void lz4(int mode, const char* filename, const unsigned char* buf, size_t size) {
|
||||
LZ4F_decompressionContext_t dctx;
|
||||
LZ4F_compressionContext_t cctx;
|
||||
LZ4F_frameInfo_t info;
|
||||
|
||||
size_t outCapacity, avail_in, ret = 0, pos = 0;
|
||||
size_t have, read;
|
||||
unsigned char *out = NULL;
|
||||
|
||||
report(mode, filename);
|
||||
int fd = open_new(filename);
|
||||
|
||||
// Initialize context
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
|
||||
break;
|
||||
case 1:
|
||||
ret = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION);
|
||||
break;
|
||||
default:
|
||||
LOGE(1, "Unsupported lz4 mode!\n");
|
||||
}
|
||||
|
||||
if (LZ4F_isError(ret))
|
||||
LOGE(1, "Context creation error: %s\n", LZ4F_getErrorName(ret));
|
||||
|
||||
// Allocate out buffer
|
||||
switch(mode) {
|
||||
case 0:
|
||||
// Read header
|
||||
read = CHUNK;
|
||||
ret = LZ4F_getFrameInfo(dctx, &info, buf, &read);
|
||||
if (LZ4F_isError(ret))
|
||||
LOGE(1, "LZ4F_getFrameInfo error: %s\n", LZ4F_getErrorName(ret));
|
||||
switch (info.blockSizeID) {
|
||||
case LZ4F_default:
|
||||
case LZ4F_max64KB: outCapacity = 1 << 16; break;
|
||||
case LZ4F_max256KB: outCapacity = 1 << 18; break;
|
||||
case LZ4F_max1MB: outCapacity = 1 << 20; break;
|
||||
case LZ4F_max4MB: outCapacity = 1 << 22; break;
|
||||
default:
|
||||
LOGE(1, "Impossible unless more block sizes are allowed\n");
|
||||
}
|
||||
pos += read;
|
||||
break;
|
||||
case 1:
|
||||
outCapacity = LZ4F_compressBound(CHUNK, NULL) + LZ4_HEADER_SIZE + LZ4_FOOTER_SIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
out = xmalloc(outCapacity);
|
||||
|
||||
// Write header
|
||||
if (mode == 1) {
|
||||
have = ret = LZ4F_compressBegin(cctx, out, size, NULL);
|
||||
if (LZ4F_isError(ret))
|
||||
LOGE(1, "Failed to start compression: error %s\n", LZ4F_getErrorName(ret));
|
||||
write_file(fd, out, have, filename);
|
||||
}
|
||||
|
||||
do {
|
||||
if (pos + CHUNK >= size) {
|
||||
avail_in = size - pos;
|
||||
} else {
|
||||
avail_in = CHUNK;
|
||||
}
|
||||
|
||||
do {
|
||||
switch(mode) {
|
||||
case 0:
|
||||
have = outCapacity, read = avail_in;
|
||||
ret = LZ4F_decompress(dctx, out, &have, buf + pos, &read, NULL);
|
||||
break;
|
||||
case 1:
|
||||
read = avail_in;
|
||||
have = ret = LZ4F_compressUpdate(cctx, out, outCapacity, buf + pos, avail_in, NULL);
|
||||
break;
|
||||
}
|
||||
if (LZ4F_isError(ret))
|
||||
LOGE(1, "LZ4 coding error: %s\n", LZ4F_getErrorName(ret));
|
||||
|
||||
write_file(fd, out, have, filename);
|
||||
// Update status
|
||||
pos += read;
|
||||
avail_in -= read;
|
||||
} while(avail_in != 0 && ret != 0);
|
||||
|
||||
} while(pos < size && ret != 0);
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
LZ4F_freeDecompressionContext(dctx);
|
||||
break;
|
||||
case 1:
|
||||
have = ret = LZ4F_compressEnd(cctx, out, outCapacity, NULL);
|
||||
if (LZ4F_isError(ret))
|
||||
LOGE(1, "Failed to end compression: error %s\n", LZ4F_getErrorName(ret));
|
||||
|
||||
write_file(fd, out, have, filename);
|
||||
|
||||
LZ4F_freeCompressionContext(cctx);
|
||||
break;
|
||||
}
|
||||
|
||||
free(out);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// Mode: 0 = decode; 1 = encode
|
||||
void bzip2(int mode, const char* filename, const unsigned char* buf, size_t size) {
|
||||
size_t ret = 0, action, have, pos = 0;
|
||||
bz_stream strm;
|
||||
char out[CHUNK];
|
||||
|
||||
report(mode, filename);
|
||||
int fd = open_new(filename);
|
||||
|
||||
strm.bzalloc = NULL;
|
||||
strm.bzfree = NULL;
|
||||
strm.opaque = NULL;
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = BZ2_bzDecompressInit(&strm, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
ret = BZ2_bzCompressInit(&strm, 9, 0, 0);
|
||||
break;
|
||||
default:
|
||||
LOGE(1, "Unsupported bzip2 mode!\n");
|
||||
}
|
||||
|
||||
if (ret != BZ_OK)
|
||||
LOGE(1, "Unable to init bzlib stream\n");
|
||||
|
||||
do {
|
||||
strm.next_in = (char *) buf + pos;
|
||||
if (pos + CHUNK >= size) {
|
||||
strm.avail_in = size - pos;
|
||||
action = BZ_FINISH;
|
||||
} else {
|
||||
strm.avail_in = CHUNK;
|
||||
action = BZ_RUN;
|
||||
}
|
||||
pos += strm.avail_in;
|
||||
|
||||
do {
|
||||
strm.avail_out = CHUNK;
|
||||
strm.next_out = out;
|
||||
switch(mode) {
|
||||
case 0:
|
||||
ret = BZ2_bzDecompress(&strm);
|
||||
break;
|
||||
case 1:
|
||||
ret = BZ2_bzCompress(&strm, action);
|
||||
break;
|
||||
}
|
||||
|
||||
have = CHUNK - strm.avail_out;
|
||||
write_file(fd, out, have, filename);
|
||||
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
} while(pos < size);
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
BZ2_bzDecompressEnd(&strm);
|
||||
break;
|
||||
case 1:
|
||||
BZ2_bzCompressEnd(&strm);
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// Mode: 0 = decode; 1 = encode
|
||||
void lz4_legacy(int mode, const char* filename, const unsigned char* buf, size_t size) {
|
||||
size_t pos = 0;
|
||||
int have;
|
||||
char *out;
|
||||
unsigned block_size, insize;
|
||||
unsigned char block_size_le[4];
|
||||
|
||||
|
||||
report(mode, filename);
|
||||
int fd = open_new(filename);
|
||||
|
||||
switch(mode) {
|
||||
case 0:
|
||||
out = xmalloc(LZ4_LEGACY_BLOCKSIZE);
|
||||
// Skip magic
|
||||
pos += 4;
|
||||
break;
|
||||
case 1:
|
||||
out = xmalloc(LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE));
|
||||
// Write magic
|
||||
write_file(fd, "\x02\x21\x4c\x18", 4, filename);
|
||||
break;
|
||||
default:
|
||||
LOGE(1, "Unsupported lz4_legacy mode!\n");
|
||||
}
|
||||
|
||||
do {
|
||||
switch(mode) {
|
||||
case 0:
|
||||
block_size = buf[pos];
|
||||
block_size += (buf[pos + 1]<<8);
|
||||
block_size += (buf[pos + 2]<<16);
|
||||
block_size += ((unsigned)buf[pos + 3])<<24;
|
||||
pos += 4;
|
||||
if (block_size > LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE))
|
||||
LOGE(1, "lz4_legacy block size too large!\n");
|
||||
have = LZ4_decompress_safe((const char*) (buf + pos), out, block_size, LZ4_LEGACY_BLOCKSIZE);
|
||||
if (have < 0)
|
||||
LOGE(1, "Cannot decode lz4_legacy block\n");
|
||||
pos += block_size;
|
||||
break;
|
||||
case 1:
|
||||
if (pos + LZ4_LEGACY_BLOCKSIZE >= size)
|
||||
insize = size - pos;
|
||||
else
|
||||
insize = LZ4_LEGACY_BLOCKSIZE;
|
||||
have = LZ4_compress_default((const char*) (buf + pos), out, insize, LZ4_COMPRESSBOUND(LZ4_LEGACY_BLOCKSIZE));
|
||||
if (have == 0)
|
||||
LOGE(1, "lz4_legacy compression error\n");
|
||||
pos += insize;
|
||||
block_size_le[0] = (unsigned char)have;
|
||||
block_size_le[1] = (unsigned char)(have >> 8);
|
||||
block_size_le[2] = (unsigned char)(have >> 16);
|
||||
block_size_le[3] = (unsigned char)(have >> 24);
|
||||
write_file(fd, block_size_le, 4, filename);
|
||||
break;
|
||||
}
|
||||
// Write main data
|
||||
write_file(fd, out, have, filename);
|
||||
} while(pos < size);
|
||||
|
||||
free(out);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int decomp(file_t type, const char *to, const unsigned char *from, size_t size) {
|
||||
switch (type) {
|
||||
case GZIP:
|
||||
gzip(0, to, from, size);
|
||||
break;
|
||||
case XZ:
|
||||
lzma(0, to, from, size);
|
||||
break;
|
||||
case LZMA:
|
||||
lzma(0, to, from, size);
|
||||
break;
|
||||
case BZIP2:
|
||||
bzip2(0, to, from, size);
|
||||
break;
|
||||
case LZ4:
|
||||
lz4(0, to, from, size);
|
||||
break;
|
||||
case LZ4_LEGACY:
|
||||
lz4_legacy(0, to, from, size);
|
||||
break;
|
||||
default:
|
||||
// Unsupported
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Output will be to.ext
|
||||
int comp(file_t type, const char *to, const unsigned char *from, size_t size) {
|
||||
char name[PATH_MAX];
|
||||
const char *ext = strrchr(to, '.');
|
||||
if (ext == NULL) ext = to;
|
||||
strcpy(name, to);
|
||||
switch (type) {
|
||||
case GZIP:
|
||||
if (strcmp(ext, ".gz") != 0)
|
||||
sprintf(name, "%s.%s", to, "gz");
|
||||
gzip(1, name, from, size);
|
||||
break;
|
||||
case XZ:
|
||||
if (strcmp(ext, ".xz") != 0)
|
||||
sprintf(name, "%s.%s", to, "xz");
|
||||
lzma(1, name, from, size);
|
||||
break;
|
||||
case LZMA:
|
||||
if (strcmp(ext, ".lzma") != 0)
|
||||
sprintf(name, "%s.%s", to, "lzma");
|
||||
lzma(2, name, from, size);
|
||||
break;
|
||||
case BZIP2:
|
||||
if (strcmp(ext, ".bz2") != 0)
|
||||
sprintf(name, "%s.%s", to, "bz2");
|
||||
bzip2(1, name, from, size);
|
||||
break;
|
||||
case LZ4:
|
||||
if (strcmp(ext, ".lz4") != 0)
|
||||
sprintf(name, "%s.%s", to, "lz4");
|
||||
lz4(1, name, from, size);
|
||||
break;
|
||||
case LZ4_LEGACY:
|
||||
if (strcmp(ext, ".lz4") != 0)
|
||||
sprintf(name, "%s.%s", to, "lz4");
|
||||
lz4_legacy(1, name, from, size);
|
||||
break;
|
||||
default:
|
||||
// Unsupported
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void decomp_file(char *from, const char *to) {
|
||||
int ok = 1;
|
||||
unsigned char *file;
|
||||
size_t size;
|
||||
mmap_ro(from, &file, &size);
|
||||
file_t type = check_type(file);
|
||||
char *ext;
|
||||
ext = strrchr(from, '.');
|
||||
if (ext == NULL)
|
||||
LOGE(1, "Bad filename extention\n");
|
||||
|
||||
// File type and extension should match
|
||||
switch (type) {
|
||||
case GZIP:
|
||||
if (strcmp(ext, ".gz") != 0)
|
||||
ok = 0;
|
||||
break;
|
||||
case XZ:
|
||||
if (strcmp(ext, ".xz") != 0)
|
||||
ok = 0;
|
||||
break;
|
||||
case LZMA:
|
||||
if (strcmp(ext, ".lzma") != 0)
|
||||
ok = 0;
|
||||
break;
|
||||
case BZIP2:
|
||||
if (strcmp(ext, ".bz2") != 0)
|
||||
ok = 0;
|
||||
break;
|
||||
case LZ4_LEGACY:
|
||||
case LZ4:
|
||||
if (strcmp(ext, ".lz4") != 0)
|
||||
ok = 0;
|
||||
break;
|
||||
default:
|
||||
LOGE(1, "Provided file \'%s\' is not a supported archive format\n", from);
|
||||
}
|
||||
if (ok) {
|
||||
// If all match, strip out the suffix
|
||||
if (!to) {
|
||||
*ext = '\0';
|
||||
to = from;
|
||||
}
|
||||
decomp(type, to, file, size);
|
||||
if (to == from) {
|
||||
*ext = '.';
|
||||
unlink(from);
|
||||
}
|
||||
} else {
|
||||
LOGE(1, "Bad filename extention \'%s\'\n", ext);
|
||||
}
|
||||
munmap(file, size);
|
||||
}
|
||||
|
||||
void comp_file(const char *method, const char *from, const char *to) {
|
||||
file_t type;
|
||||
if (strcmp(method, "gzip") == 0) {
|
||||
type = GZIP;
|
||||
} else if (strcmp(method, "xz") == 0) {
|
||||
type = XZ;
|
||||
} else if (strcmp(method, "lzma") == 0) {
|
||||
type = LZMA;
|
||||
} else if (strcmp(method, "lz4") == 0) {
|
||||
type = LZ4;
|
||||
} else if (strcmp(method, "lz4_legacy") == 0) {
|
||||
type = LZ4_LEGACY;
|
||||
} else if (strcmp(method, "bzip2") == 0) {
|
||||
type = BZIP2;
|
||||
} else {
|
||||
fprintf(stderr, "Only support following methods: ");
|
||||
for (int i = 0; SUP_LIST[i]; ++i)
|
||||
fprintf(stderr, "%s ", SUP_LIST[i]);
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
unsigned char *file;
|
||||
size_t size;
|
||||
mmap_ro(from, &file, &size);
|
||||
if (!to)
|
||||
to = from;
|
||||
comp(type, to, file, size);
|
||||
munmap(file, size);
|
||||
if (to == from)
|
||||
unlink(from);
|
||||
}
|
||||
|
||||
536
jni/magiskboot/cpio.c
Normal file
536
jni/magiskboot/cpio.c
Normal file
@@ -0,0 +1,536 @@
|
||||
#include "magiskboot.h"
|
||||
#include "cpio.h"
|
||||
#include "vector.h"
|
||||
#include "list.h"
|
||||
|
||||
static uint32_t x8u(char *hex) {
|
||||
uint32_t val, inpos = 8, outpos;
|
||||
char pattern[6];
|
||||
|
||||
while (*hex == '0') {
|
||||
hex++;
|
||||
if (!--inpos) return 0;
|
||||
}
|
||||
// Because scanf gratuitously treats %*X differently than printf does.
|
||||
sprintf(pattern, "%%%dx%%n", inpos);
|
||||
sscanf(hex, pattern, &val, &outpos);
|
||||
if (inpos != outpos) LOGE(1, "bad cpio header\n");
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void cpio_free(cpio_file *f) {
|
||||
if (f) {
|
||||
free(f->filename);
|
||||
free(f->data);
|
||||
free(f);
|
||||
}
|
||||
}
|
||||
|
||||
static void cpio_vec_insert(struct vector *v, cpio_file *n) {
|
||||
cpio_file *f, *t;
|
||||
int shift = 0;
|
||||
// Insert in alphabet order
|
||||
vec_for_each(v, f) {
|
||||
if (shift) {
|
||||
vec_entry(v)[_] = t;
|
||||
t = f;
|
||||
continue;
|
||||
}
|
||||
t = f;
|
||||
if (strcmp(f->filename, n->filename) == 0) {
|
||||
// Replace, then all is done
|
||||
cpio_free(f);
|
||||
vec_entry(v)[_] = n;
|
||||
return;
|
||||
} else if (strcmp(f->filename, n->filename) > 0) {
|
||||
// Insert, then start shifting
|
||||
vec_entry(v)[_] = n;
|
||||
t = f;
|
||||
shift = 1;
|
||||
}
|
||||
}
|
||||
if (shift)
|
||||
vec_push_back(v, t);
|
||||
else
|
||||
vec_push_back(v, n);
|
||||
}
|
||||
|
||||
static int cpio_compare(const void *a, const void *b) {
|
||||
return strcmp((*(cpio_file **) a)->filename, (*(cpio_file **) b)->filename);
|
||||
}
|
||||
|
||||
// Parse cpio file to a vector of cpio_file
|
||||
static void parse_cpio(const char *filename, struct vector *v) {
|
||||
fprintf(stderr, "Loading cpio: [%s]\n\n", filename);
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
cpio_newc_header header;
|
||||
cpio_file *f;
|
||||
while(read(fd, &header, 110) == 110) {
|
||||
f = xcalloc(sizeof(*f), 1);
|
||||
// f->ino = x8u(header.ino);
|
||||
f->mode = x8u(header.mode);
|
||||
f->uid = x8u(header.uid);
|
||||
f->gid = x8u(header.gid);
|
||||
// f->nlink = x8u(header.nlink);
|
||||
// f->mtime = x8u(header.mtime);
|
||||
f->filesize = x8u(header.filesize);
|
||||
// f->devmajor = x8u(header.devmajor);
|
||||
// f->devminor = x8u(header.devminor);
|
||||
// f->rdevmajor = x8u(header.rdevmajor);
|
||||
// f->rdevminor = x8u(header.rdevminor);
|
||||
f->namesize = x8u(header.namesize);
|
||||
// f->check = x8u(header.check);
|
||||
f->filename = malloc(f->namesize);
|
||||
xxread(fd, f->filename, f->namesize);
|
||||
file_align(fd, 4, 0);
|
||||
if (strcmp(f->filename, ".") == 0 || strcmp(f->filename, "..") == 0) {
|
||||
cpio_free(f);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(f->filename, "TRAILER!!!") == 0) {
|
||||
cpio_free(f);
|
||||
break;
|
||||
}
|
||||
if (f->filesize) {
|
||||
f->data = malloc(f->filesize);
|
||||
xxread(fd, f->data, f->filesize);
|
||||
file_align(fd, 4, 0);
|
||||
}
|
||||
vec_push_back(v, f);
|
||||
}
|
||||
close(fd);
|
||||
// Sort by name
|
||||
vec_sort(v, cpio_compare);
|
||||
}
|
||||
|
||||
static void dump_cpio(const char *filename, struct vector *v) {
|
||||
fprintf(stderr, "\nDump cpio: [%s]\n\n", filename);
|
||||
int fd = open_new(filename);
|
||||
unsigned inode = 300000;
|
||||
char header[111];
|
||||
cpio_file *f;
|
||||
vec_for_each(v, f) {
|
||||
if (f->remove) continue;
|
||||
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
|
||||
inode++, // f->ino
|
||||
f->mode,
|
||||
f->uid,
|
||||
f->gid,
|
||||
1, // f->nlink
|
||||
0, // f->mtime
|
||||
f->filesize,
|
||||
0, // f->devmajor
|
||||
0, // f->devminor
|
||||
0, // f->rdevmajor
|
||||
0, // f->rdevminor
|
||||
f->namesize,
|
||||
0 // f->check
|
||||
);
|
||||
xwrite(fd, header, 110);
|
||||
xwrite(fd, f->filename, f->namesize);
|
||||
file_align(fd, 4, 1);
|
||||
if (f->filesize) {
|
||||
xwrite(fd, f->data, f->filesize);
|
||||
file_align(fd, 4, 1);
|
||||
}
|
||||
}
|
||||
// Write trailer
|
||||
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", inode++, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 11, 0);
|
||||
xwrite(fd, header, 110);
|
||||
xwrite(fd, "TRAILER!!!\0", 11);
|
||||
file_align(fd, 4, 1);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void cpio_vec_destroy(struct vector *v) {
|
||||
// Free each cpio_file
|
||||
cpio_file *f;
|
||||
vec_for_each(v, f) {
|
||||
cpio_free(f);
|
||||
}
|
||||
vec_destroy(v);
|
||||
}
|
||||
|
||||
static void cpio_rm(int recursive, const char *entry, struct vector *v) {
|
||||
cpio_file *f;
|
||||
vec_for_each(v, f) {
|
||||
if ((recursive && strncmp(f->filename, entry, strlen(entry)) == 0)
|
||||
|| (strcmp(f->filename, entry) == 0) ) {
|
||||
if (!f->remove) {
|
||||
fprintf(stderr, "Remove [%s]\n", entry);
|
||||
f->remove = 1;
|
||||
}
|
||||
if (!recursive) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cpio_mkdir(mode_t mode, const char *entry, struct vector *v) {
|
||||
cpio_file *f = xcalloc(sizeof(*f), 1);
|
||||
f->mode = S_IFDIR | mode;
|
||||
f->namesize = strlen(entry) + 1;
|
||||
f->filename = xmalloc(f->namesize);
|
||||
memcpy(f->filename, entry, f->namesize);
|
||||
cpio_vec_insert(v, f);
|
||||
fprintf(stderr, "Create directory [%s] (%04o)\n",entry, mode);
|
||||
}
|
||||
|
||||
static void cpio_add(mode_t mode, const char *entry, const char *filename, struct vector *v) {
|
||||
int fd = xopen(filename, O_RDONLY);
|
||||
cpio_file *f = xcalloc(sizeof(*f), 1);
|
||||
f->mode = S_IFREG | mode;
|
||||
f->namesize = strlen(entry) + 1;
|
||||
f->filename = xmalloc(f->namesize);
|
||||
memcpy(f->filename, entry, f->namesize);
|
||||
f->filesize = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
f->data = malloc(f->filesize);
|
||||
xxread(fd, f->data, f->filesize);
|
||||
close(fd);
|
||||
cpio_vec_insert(v, f);
|
||||
fprintf(stderr, "Add entry [%s] (%04o)\n", entry, mode);
|
||||
}
|
||||
|
||||
static void cpio_test(struct vector *v) {
|
||||
#define MAGISK_PATCH 0x1
|
||||
#define OTHER_PATCH 0x2
|
||||
int ret = 0;
|
||||
cpio_file *f;
|
||||
const char *OTHER_LIST[] = { "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc", "init.supersu.rc", NULL };
|
||||
vec_for_each(v, f) {
|
||||
for (int i = 0; OTHER_LIST[i]; ++i) {
|
||||
if (strcmp(f->filename, OTHER_LIST[i]) == 0)
|
||||
ret |= OTHER_PATCH;
|
||||
}
|
||||
if (strcmp(f->filename, "init.magisk.rc") == 0)
|
||||
ret |= MAGISK_PATCH;
|
||||
}
|
||||
cpio_vec_destroy(v);
|
||||
exit((ret & OTHER_PATCH) ? OTHER_PATCH : (ret & MAGISK_PATCH));
|
||||
}
|
||||
|
||||
static int check_verity_pattern(const char *s) {
|
||||
int pos = 0;
|
||||
if (s[0] == ',') ++pos;
|
||||
if (strncmp(s + pos, "verify", 6) != 0) return -1;
|
||||
pos += 6;
|
||||
if (s[pos] == '=') {
|
||||
while (s[pos] != ' ' && s[pos] != '\n' && s[pos] != ',') ++pos;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
static struct list_head *block_to_list(char *data) {
|
||||
struct list_head *head = xmalloc(sizeof(*head));
|
||||
line_list *line;
|
||||
init_list_head(head);
|
||||
char *tok;
|
||||
tok = strsep(&data, "\n");
|
||||
while (tok) {
|
||||
line = xcalloc(sizeof(*line), 1);
|
||||
line->line = tok;
|
||||
list_insert_end(head, &line->pos);
|
||||
tok = strsep(&data, "\n");
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
static char *list_to_block(struct list_head *head, uint32_t filesize) {
|
||||
line_list *line;
|
||||
char *data = xmalloc(filesize);
|
||||
uint32_t off = 0;
|
||||
list_for_each(line, head, line_list, pos) {
|
||||
strcpy(data + off, line->line);
|
||||
off += strlen(line->line);
|
||||
data[off++] = '\n';
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static void free_newline(line_list *line) {
|
||||
if (line->isNew)
|
||||
free(line->line);
|
||||
}
|
||||
|
||||
static void cpio_patch(struct vector *v, int keepverity, int keepforceencrypt) {
|
||||
struct list_head *head;
|
||||
line_list *line;
|
||||
cpio_file *f;
|
||||
int skip, injected = 0;
|
||||
size_t read, write;
|
||||
const char *ENCRYPT_LIST[] = { "forceencrypt", "forcefdeorfbe", "fileencryptioninline", NULL };
|
||||
vec_for_each(v, f) {
|
||||
if (strcmp(f->filename, "init.rc") == 0) {
|
||||
head = block_to_list(f->data);
|
||||
list_for_each(line, head, line_list, pos) {
|
||||
if (strstr(line->line, "import")) {
|
||||
if (strstr(line->line, "init.magisk.rc"))
|
||||
injected = 1;
|
||||
if (injected)
|
||||
continue;
|
||||
// Inject magisk script as import
|
||||
fprintf(stderr, "Inject new line [import /init.magisk.rc] in [init.rc]\n");
|
||||
line = xcalloc(sizeof(*line), 1);
|
||||
line->line = strdup("import /init.magisk.rc");
|
||||
line->isNew = 1;
|
||||
f->filesize += 23;
|
||||
list_insert(__->prev, &line->pos);
|
||||
injected = 1;
|
||||
} else if (strstr(line->line, "selinux.reload_policy")) {
|
||||
// Remove this line
|
||||
fprintf(stderr, "Remove line [%s] in [init.rc]\n", line->line);
|
||||
f->filesize -= strlen(line->line) + 1;
|
||||
__ = list_pop(&line->pos);
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
char *temp = list_to_block(head, f->filesize);
|
||||
free(f->data);
|
||||
f->data = temp;
|
||||
list_destory(head, list_head, pos, free_newline);
|
||||
free(head);
|
||||
} else {
|
||||
if (!keepverity) {
|
||||
if (strstr(f->filename, "fstab") != NULL && S_ISREG(f->mode)) {
|
||||
for (read = 0, write = 0; read < f->filesize; ++read, ++write) {
|
||||
skip = check_verity_pattern(f->data + read);
|
||||
if (skip > 0) {
|
||||
fprintf(stderr, "Remove pattern [%.*s] in [%s]\n", skip, f->data + read, f->filename);
|
||||
read += skip;
|
||||
}
|
||||
f->data[write] = f->data[read];
|
||||
}
|
||||
f->filesize = write;
|
||||
} else if (strcmp(f->filename, "verity_key") == 0) {
|
||||
fprintf(stderr, "Remove [verity_key]\n");
|
||||
f->remove = 1;
|
||||
}
|
||||
}
|
||||
if (!keepforceencrypt) {
|
||||
if (strstr(f->filename, "fstab") != NULL && S_ISREG(f->mode)) {
|
||||
for (read = 0, write = 0; read < f->filesize; ++read, ++write) {
|
||||
for (int i = 0 ; ENCRYPT_LIST[i]; ++i) {
|
||||
if (strncmp(f->data + read, ENCRYPT_LIST[i], strlen(ENCRYPT_LIST[i])) == 0) {
|
||||
memcpy(f->data + write, "encryptable", 11);
|
||||
fprintf(stderr, "Replace [%s] with [%s] in [%s]\n", ENCRYPT_LIST[i], "encryptable", f->filename);
|
||||
write += 11;
|
||||
read += strlen(ENCRYPT_LIST[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
f->data[write] = f->data[read];
|
||||
}
|
||||
f->filesize = write;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cpio_extract(const char *entry, const char *filename, struct vector *v) {
|
||||
cpio_file *f;
|
||||
vec_for_each(v, f) {
|
||||
if (strcmp(f->filename, entry) == 0 && S_ISREG(f->mode)) {
|
||||
fprintf(stderr, "Extracting [%s] to [%s]\n\n", entry, filename);
|
||||
int fd = open_new(filename);
|
||||
xwrite(fd, f->data, f->filesize);
|
||||
fchmod(fd, f->mode);
|
||||
fchown(fd, f->uid, f->gid);
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
LOGE(1, "Cannot find the file entry [%s]\n", entry);
|
||||
}
|
||||
|
||||
static void cpio_backup(const char *orig, struct vector *v) {
|
||||
struct vector o_body, *o = &o_body, bak;
|
||||
cpio_file *m, *n, *dir, *rem;
|
||||
char buf[PATH_MAX];
|
||||
int res, doBak;
|
||||
|
||||
dir = xcalloc(sizeof(*dir), 1);
|
||||
rem = xcalloc(sizeof(*rem), 1);
|
||||
vec_init(o);
|
||||
vec_init(&bak);
|
||||
// First push back the directory and the rmlist
|
||||
vec_push_back(&bak, dir);
|
||||
vec_push_back(&bak, rem);
|
||||
parse_cpio(orig, o);
|
||||
// Remove possible backups in original ramdisk
|
||||
cpio_rm(1, ".backup", o);
|
||||
cpio_rm(1, ".backup", v);
|
||||
|
||||
// Init the directory and rmlist
|
||||
dir->filename = strdup(".backup");
|
||||
dir->namesize = strlen(dir->filename) + 1;
|
||||
dir->mode = S_IFDIR;
|
||||
rem->filename = strdup(".backup/.rmlist");
|
||||
rem->namesize = strlen(rem->filename) + 1;
|
||||
rem->mode = S_IFREG;
|
||||
|
||||
// Start comparing
|
||||
size_t i = 0, j = 0;
|
||||
while(i != vec_size(o) || j != vec_size(v)) {
|
||||
doBak = 0;
|
||||
if (i != vec_size(o) && j != vec_size(v)) {
|
||||
m = vec_entry(o)[i];
|
||||
n = vec_entry(v)[j];
|
||||
res = strcmp(m->filename, n->filename);
|
||||
} else if (i == vec_size(o)) {
|
||||
n = vec_entry(v)[j];
|
||||
res = 1;
|
||||
} else if (j == vec_size(v)) {
|
||||
m = vec_entry(o)[i];
|
||||
res = -1;
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
// Something is missing in new ramdisk, backup!
|
||||
++i;
|
||||
doBak = 1;
|
||||
fprintf(stderr, "Backup missing entry: ");
|
||||
} else if (res == 0) {
|
||||
++i; ++j;
|
||||
if (m->filesize == n->filesize && memcmp(m->data, n->data, m->filesize) == 0)
|
||||
continue;
|
||||
// Not the same!
|
||||
doBak = 1;
|
||||
fprintf(stderr, "Backup mismatch entry: ");
|
||||
} else {
|
||||
// Someting new in ramdisk, record in rem
|
||||
++j;
|
||||
if (n->remove) continue;
|
||||
rem->data = xrealloc(rem->data, rem->filesize + n->namesize);
|
||||
memcpy(rem->data + rem->filesize, n->filename, n->namesize);
|
||||
rem->filesize += n->namesize;
|
||||
fprintf(stderr, "Record new entry: [%s] -> [.backup/.rmlist]\n", n->filename);
|
||||
}
|
||||
if (doBak) {
|
||||
m->namesize += 8;
|
||||
m->filename = realloc(m->filename, m->namesize);
|
||||
strcpy(buf, m->filename);
|
||||
sprintf(m->filename, ".backup/%s", buf);
|
||||
fprintf(stderr, "[%s] -> [%s]\n", buf, m->filename);
|
||||
vec_push_back(&bak, m);
|
||||
// NULL the original entry, so it won't be freed
|
||||
vec_entry(o)[i - 1] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the backup files to the original ramdisk
|
||||
vec_for_each(&bak, m) {
|
||||
vec_push_back(v, m);
|
||||
}
|
||||
|
||||
// Don't include if empty
|
||||
if (rem->filesize == 0) {
|
||||
rem->remove = 1;
|
||||
if (bak.size == 2)
|
||||
dir->remove = 1;
|
||||
}
|
||||
|
||||
// Sort
|
||||
vec_sort(v, cpio_compare);
|
||||
|
||||
// Cleanup
|
||||
cpio_vec_destroy(o);
|
||||
}
|
||||
|
||||
static int cpio_restore(struct vector *v) {
|
||||
cpio_file *f, *n;
|
||||
int ret = 1;
|
||||
vec_for_each(v, f) {
|
||||
if (strstr(f->filename, ".backup") != NULL) {
|
||||
ret = 0;
|
||||
f->remove = 1;
|
||||
if (strcmp(f->filename, ".backup") == 0) continue;
|
||||
if (strcmp(f->filename, ".backup/.rmlist") == 0) {
|
||||
for (int pos = 0; pos < f->filesize; pos += strlen(f->data + pos) + 1)
|
||||
cpio_rm(0, f->data + pos, v);
|
||||
continue;
|
||||
}
|
||||
n = xcalloc(sizeof(*n), 1);
|
||||
memcpy(n, f, sizeof(*f));
|
||||
n->namesize -= 8;
|
||||
n->filename = xmalloc(n->namesize);
|
||||
memcpy(n->filename, f->filename + 8, n->namesize);
|
||||
n->data = xmalloc(n->filesize);
|
||||
memcpy(n->data, f->data, n->filesize);
|
||||
n->remove = 0;
|
||||
fprintf(stderr, "Restoring [%s] -> [%s]\n", f->filename, n->filename);
|
||||
cpio_vec_insert(v, n);
|
||||
}
|
||||
}
|
||||
// Some known stuff we can remove
|
||||
cpio_rm(0, "sbin/magic_mask.sh", v);
|
||||
cpio_rm(0, "init.magisk.rc", v);
|
||||
cpio_rm(0, "magisk", v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpio_commands(const char *command, int argc, char *argv[]) {
|
||||
int recursive = 0, ret = 0;
|
||||
command_t cmd;
|
||||
char *incpio = argv[0];
|
||||
++argv;
|
||||
--argc;
|
||||
if (strcmp(command, "test") == 0) {
|
||||
cmd = TEST;
|
||||
} else if (strcmp(command, "restore") == 0) {
|
||||
cmd = RESTORE;
|
||||
} else if (argc == 1 && strcmp(command, "backup") == 0) {
|
||||
cmd = BACKUP;
|
||||
} else if (argc > 0 && strcmp(command, "rm") == 0) {
|
||||
cmd = RM;
|
||||
if (argc == 2 && strcmp(argv[0], "-r") == 0) {
|
||||
recursive = 1;
|
||||
++argv;
|
||||
--argc;
|
||||
}
|
||||
} else if (argc == 2 && strcmp(command, "patch") == 0) {
|
||||
cmd = PATCH;
|
||||
} else if (argc == 2 && strcmp(command, "extract") == 0) {
|
||||
cmd = EXTRACT;
|
||||
} else if (argc == 2 && strcmp(command, "mkdir") == 0) {
|
||||
cmd = MKDIR;
|
||||
} else if (argc == 3 && strcmp(command, "add") == 0) {
|
||||
cmd = ADD;
|
||||
} else {
|
||||
cmd = NONE;
|
||||
}
|
||||
struct vector v;
|
||||
vec_init(&v);
|
||||
parse_cpio(incpio, &v);
|
||||
switch(cmd) {
|
||||
case TEST:
|
||||
cpio_test(&v);
|
||||
break;
|
||||
case RESTORE:
|
||||
ret = cpio_restore(&v);
|
||||
break;
|
||||
case BACKUP:
|
||||
cpio_backup(argv[0], &v);
|
||||
case RM:
|
||||
cpio_rm(recursive, argv[0], &v);
|
||||
break;
|
||||
case PATCH:
|
||||
cpio_patch(&v, strcmp(argv[0], "true") == 0, strcmp(argv[1], "true") == 0);
|
||||
break;
|
||||
case EXTRACT:
|
||||
cpio_extract(argv[0], argv[1], &v);
|
||||
break;
|
||||
case MKDIR:
|
||||
cpio_mkdir(strtoul(argv[0], NULL, 8), argv[1], &v);
|
||||
break;
|
||||
case ADD:
|
||||
cpio_add(strtoul(argv[0], NULL, 8), argv[1], argv[2], &v);
|
||||
break;
|
||||
case NONE:
|
||||
return 1;
|
||||
}
|
||||
dump_cpio(incpio, &v);
|
||||
cpio_vec_destroy(&v);
|
||||
exit(ret);
|
||||
}
|
||||
50
jni/magiskboot/cpio.h
Normal file
50
jni/magiskboot/cpio.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef _CPIO_H_
|
||||
#define _CPIO_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
typedef struct cpio_file {
|
||||
// uint32_t ino;
|
||||
uint32_t mode;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
// uint32_t nlink;
|
||||
// uint32_t mtime;
|
||||
uint32_t filesize;
|
||||
// uint32_t devmajor;
|
||||
// uint32_t devminor;
|
||||
// uint32_t rdevmajor;
|
||||
// uint32_t rdevminor;
|
||||
uint32_t namesize;
|
||||
// uint32_t check;
|
||||
char *filename;
|
||||
char *data;
|
||||
int remove;
|
||||
} cpio_file;
|
||||
|
||||
typedef struct line_list {
|
||||
char *line;
|
||||
int isNew;
|
||||
struct list_head pos;
|
||||
} line_list;
|
||||
|
||||
typedef struct cpio_newc_header {
|
||||
char magic[6];
|
||||
char ino[8];
|
||||
char mode[8];
|
||||
char uid[8];
|
||||
char gid[8];
|
||||
char nlink[8];
|
||||
char mtime[8];
|
||||
char filesize[8];
|
||||
char devmajor[8];
|
||||
char devminor[8];
|
||||
char rdevmajor[8];
|
||||
char rdevminor[8];
|
||||
char namesize[8];
|
||||
char check[8];
|
||||
} cpio_newc_header;
|
||||
|
||||
#endif
|
||||
32
jni/magiskboot/hexpatch.c
Normal file
32
jni/magiskboot/hexpatch.c
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "magiskboot.h"
|
||||
|
||||
static void hex2byte(const char *hex, unsigned char *str) {
|
||||
char high, low;
|
||||
for (int i = 0, length = strlen(hex); i < length; i += 2) {
|
||||
high = toupper(hex[i]) - '0';
|
||||
low = toupper(hex[i + 1]) - '0';
|
||||
str[i / 2] = ((high > 9 ? high - 7 : high) << 4) + (low > 9 ? low - 7 : low);
|
||||
}
|
||||
}
|
||||
|
||||
void hexpatch(const char *image, const char *from, const char *to) {
|
||||
int patternsize = strlen(from) / 2, patchsize = strlen(to) / 2;
|
||||
size_t filesize;
|
||||
unsigned char *file, *pattern, *patch;
|
||||
mmap_rw(image, &file, &filesize);
|
||||
pattern = xmalloc(patternsize);
|
||||
patch = xmalloc(patchsize);
|
||||
hex2byte(from, pattern);
|
||||
hex2byte(to, patch);
|
||||
for (size_t i = 0; i < filesize - patternsize; ++i) {
|
||||
if (memcmp(file + i, pattern, patternsize) == 0) {
|
||||
fprintf(stderr, "Pattern %s found!\nPatching to %s\n", from, to);
|
||||
memset(file + i, 0, patternsize);
|
||||
memcpy(file + i, patch, patchsize);
|
||||
i += patternsize - 1;
|
||||
}
|
||||
}
|
||||
munmap(file, filesize);
|
||||
free(pattern);
|
||||
free(patch);
|
||||
}
|
||||
10
jni/magiskboot/magisk.h
Normal file
10
jni/magiskboot/magisk.h
Normal file
@@ -0,0 +1,10 @@
|
||||
/* magisk.h - Let MagiskBoot use the same error handling API as main magisk program
|
||||
*/
|
||||
|
||||
#ifndef _MAGISK_H_
|
||||
#define _MAGISK_H_
|
||||
|
||||
#define LOGE(err, ...) { fprintf(stderr, __VA_ARGS__); exit(err); }
|
||||
#define PLOGE(fmt, args...) { fprintf(stderr, fmt " failed with %d: %s\n\n", ##args, errno, strerror(errno)); exit(1); }
|
||||
|
||||
#endif
|
||||
93
jni/magiskboot/magiskboot.h
Normal file
93
jni/magiskboot/magiskboot.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#ifndef _MAGISKBOOT_H_
|
||||
#define _MAGISKBOOT_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bootimg.h"
|
||||
#include "sha1.h"
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define CHROMEOS_MAGIC "CHROMEOS"
|
||||
#define ELF32_MAGIC "\x7f""ELF\x01"
|
||||
#define ELF64_MAGIC "\x7f""ELF\x02"
|
||||
|
||||
#define KERNEL_FILE "kernel"
|
||||
#define RAMDISK_FILE "ramdisk.cpio"
|
||||
#define SECOND_FILE "second"
|
||||
#define DTB_FILE "dtb"
|
||||
#define NEW_BOOT "new-boot.img"
|
||||
|
||||
#define str(a) #a
|
||||
#define xstr(a) str(a)
|
||||
|
||||
typedef enum {
|
||||
UNKNOWN,
|
||||
CHROMEOS,
|
||||
AOSP,
|
||||
ELF32,
|
||||
ELF64,
|
||||
GZIP,
|
||||
LZOP,
|
||||
XZ,
|
||||
LZMA,
|
||||
BZIP2,
|
||||
LZ4,
|
||||
LZ4_LEGACY,
|
||||
MTK
|
||||
} file_t;
|
||||
|
||||
typedef enum {
|
||||
NONE,
|
||||
RM,
|
||||
MKDIR,
|
||||
ADD,
|
||||
EXTRACT,
|
||||
TEST,
|
||||
PATCH,
|
||||
BACKUP,
|
||||
RESTORE
|
||||
} command_t;
|
||||
|
||||
extern char *SUP_LIST[];
|
||||
extern char *SUP_EXT_LIST[];
|
||||
extern file_t SUP_TYPE_LIST[];
|
||||
|
||||
// Main entries
|
||||
void unpack(const char *image);
|
||||
void repack(const char* orig_image, const char* out_image);
|
||||
void hexpatch(const char *image, const char *from, const char *to);
|
||||
int parse_img(unsigned char *orig, size_t size);
|
||||
int cpio_commands(const char *command, int argc, char *argv[]);
|
||||
void cleanup();
|
||||
|
||||
// Compressions
|
||||
void gzip(int mode, const char* filename, const unsigned char* buf, size_t size);
|
||||
void lzma(int mode, const char* filename, const unsigned char* buf, size_t size);
|
||||
void lz4(int mode, const char* filename, const unsigned char* buf, size_t size);
|
||||
void bzip2(int mode, const char* filename, const unsigned char* buf, size_t size);
|
||||
void lz4_legacy(int mode, const char* filename, const unsigned char* buf, size_t size);
|
||||
int comp(file_t type, const char *to, const unsigned char *from, size_t size);
|
||||
void comp_file(const char *method, const char *from, const char *to);
|
||||
int decomp(file_t type, const char *to, const unsigned char *from, size_t size);
|
||||
void decomp_file(char *from, const char *to);
|
||||
|
||||
// Utils
|
||||
void mmap_ro(const char *filename, unsigned char **buf, size_t *size);
|
||||
void mmap_rw(const char *filename, unsigned char **buf, size_t *size);
|
||||
file_t check_type(const unsigned char *buf);
|
||||
void write_zero(int fd, size_t size);
|
||||
void mem_align(size_t *pos, size_t align);
|
||||
void file_align(int fd, size_t align, int out);
|
||||
int open_new(const char *filename);
|
||||
|
||||
#endif
|
||||
99
jni/magiskboot/main.c
Normal file
99
jni/magiskboot/main.c
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "magiskboot.h"
|
||||
|
||||
/********************
|
||||
Patch Boot Image
|
||||
*********************/
|
||||
|
||||
static void usage(char *arg0) {
|
||||
fprintf(stderr,
|
||||
"%s --unpack <bootimg>\n"
|
||||
" Unpack <bootimg> to kernel, ramdisk.cpio, (second), (dtb) into the\n current directory\n"
|
||||
"\n"
|
||||
"%s --repack <origbootimg> [outbootimg]\n"
|
||||
" Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory\n"
|
||||
" to [outbootimg], or new-boot.img if not specified.\n"
|
||||
" It will compress ramdisk.cpio with the same method used in <origbootimg>\n"
|
||||
" if exists, or attempt to find ramdisk.cpio.[ext], and repack\n"
|
||||
" directly with the compressed ramdisk file\n"
|
||||
"\n"
|
||||
"%s --hexpatch <file> <hexpattern1> <hexpattern2>\n"
|
||||
" Search <hexpattern1> in <file>, and replace with <hexpattern2>\n"
|
||||
"\n"
|
||||
"%s --cpio-<cmd> <incpio> [flags...] [params...]\n"
|
||||
" Do cpio related cmds to <incpio> (modifications are done directly)\n Supported commands:\n"
|
||||
" --cpio-rm <incpio> [-r] <entry>\n Remove entry from cpio, flag -r to remove recursively\n"
|
||||
" --cpio-mkdir <incpio> <mode> <entry>\n Create directory as an <entry>\n"
|
||||
" --cpio-add <incpio> <mode> <entry> <infile>\n Add <infile> as an <entry>; replaces <entry> if already exists\n"
|
||||
" --cpio-extract <incpio> <entry> <outfile>\n Extract <entry> to <outfile>\n"
|
||||
" --cpio-test <incpio>\n Return value: 0/not patched 1/Magisk 2/Other (e.g. phh, SuperSU)\n"
|
||||
" --cpio-patch <KEEPVERITY> <KEEPFORCEENCRYPT>\n Patch cpio for Magisk. KEEP**** are true/false values\n"
|
||||
" --cpio-backup <incpio> <origcpio>\n Create ramdisk backups into <incpio> from <origcpio>\n"
|
||||
" --cpio-restore <incpio>\n Restore ramdisk from ramdisk backup within <incpio>\n"
|
||||
"\n"
|
||||
"%s --compress[=method] <infile> [outfile]\n"
|
||||
" Compress <infile> with [method] (default: gzip), optionally to [outfile]\n Supported methods: "
|
||||
, arg0, arg0, arg0, arg0, arg0);
|
||||
for (int i = 0; SUP_LIST[i]; ++i)
|
||||
fprintf(stderr, "%s ", SUP_LIST[i]);
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
"\n"
|
||||
"%s --decompress <infile> [outfile]\n"
|
||||
" Detect method and decompress <infile>, optionally to [outfile]\n Supported methods: "
|
||||
, arg0);
|
||||
for (int i = 0; SUP_LIST[i]; ++i)
|
||||
fprintf(stderr, "%s ", SUP_LIST[i]);
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
"\n"
|
||||
"%s --sha1 <file>\n"
|
||||
" Print the SHA1 checksum for <file>\n"
|
||||
"\n"
|
||||
"%s --cleanup\n"
|
||||
" Cleanup the current working directory\n"
|
||||
"\n"
|
||||
, arg0, arg0);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
fprintf(stderr, "MagiskBoot v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - Boot Image Modification Tool\n\n");
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "--cleanup") == 0) {
|
||||
cleanup();
|
||||
} else if (argc > 2 && strcmp(argv[1], "--sha1") == 0) {
|
||||
char sha1[21], *buf;
|
||||
size_t size;
|
||||
mmap_ro(argv[2], (unsigned char **) &buf, &size);
|
||||
SHA1(sha1, buf, size);
|
||||
for (int i = 0; i < 20; ++i)
|
||||
fprintf(stderr, "%02x", sha1[i]);
|
||||
fprintf(stderr, "\n");
|
||||
munmap(buf, size);
|
||||
} else if (argc > 2 && strcmp(argv[1], "--unpack") == 0) {
|
||||
unpack(argv[2]);
|
||||
} else if (argc > 2 && strcmp(argv[1], "--repack") == 0) {
|
||||
repack(argv[2], argc > 3 ? argv[3] : NEW_BOOT);
|
||||
} else if (argc > 2 && strcmp(argv[1], "--decompress") == 0) {
|
||||
decomp_file(argv[2], argc > 3 ? argv[3] : NULL);
|
||||
} else if (argc > 2 && strncmp(argv[1], "--compress", 10) == 0) {
|
||||
char *method;
|
||||
method = strchr(argv[1], '=');
|
||||
if (method == NULL) method = "gzip";
|
||||
else method++;
|
||||
comp_file(method, argv[2], argc > 3 ? argv[3] : NULL);
|
||||
} else if (argc > 4 && strcmp(argv[1], "--hexpatch") == 0) {
|
||||
hexpatch(argv[2], argv[3], argv[4]);
|
||||
} else if (argc > 2 && strncmp(argv[1], "--cpio", 6) == 0) {
|
||||
char *command;
|
||||
command = strchr(argv[1] + 2, '-');
|
||||
if (command == NULL) usage(argv[0]);
|
||||
else ++command;
|
||||
if (cpio_commands(command, argc - 2, argv + 2)) usage(argv[0]);
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
295
jni/magiskboot/sha1.c
Normal file
295
jni/magiskboot/sha1.c
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
|
||||
/* #define SHA1HANDSOFF * Copies data before messing with it. */
|
||||
|
||||
#define SHA1HANDSOFF
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* for uint32_t */
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||
|(rol(block->l[i],8)&0x00FF00FF))
|
||||
#elif BYTE_ORDER == BIG_ENDIAN
|
||||
#define blk0(i) block->l[i]
|
||||
#else
|
||||
#error "Endianness not defined!"
|
||||
#endif
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
|
||||
void SHA1Transform(
|
||||
uint32_t state[5],
|
||||
const unsigned char buffer[64]
|
||||
)
|
||||
{
|
||||
uint32_t a, b, c, d, e;
|
||||
|
||||
typedef union
|
||||
{
|
||||
unsigned char c[64];
|
||||
uint32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
|
||||
#ifdef SHA1HANDSOFF
|
||||
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||
|
||||
memcpy(block, buffer, 64);
|
||||
#else
|
||||
/* The following had better never be used because it causes the
|
||||
* pointer-to-const buffer to be cast into a pointer to non-const.
|
||||
* And the result is written through. I threw a "const" in, hoping
|
||||
* this will cause a diagnostic.
|
||||
*/
|
||||
CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer;
|
||||
#endif
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a, b, c, d, e, 0);
|
||||
R0(e, a, b, c, d, 1);
|
||||
R0(d, e, a, b, c, 2);
|
||||
R0(c, d, e, a, b, 3);
|
||||
R0(b, c, d, e, a, 4);
|
||||
R0(a, b, c, d, e, 5);
|
||||
R0(e, a, b, c, d, 6);
|
||||
R0(d, e, a, b, c, 7);
|
||||
R0(c, d, e, a, b, 8);
|
||||
R0(b, c, d, e, a, 9);
|
||||
R0(a, b, c, d, e, 10);
|
||||
R0(e, a, b, c, d, 11);
|
||||
R0(d, e, a, b, c, 12);
|
||||
R0(c, d, e, a, b, 13);
|
||||
R0(b, c, d, e, a, 14);
|
||||
R0(a, b, c, d, e, 15);
|
||||
R1(e, a, b, c, d, 16);
|
||||
R1(d, e, a, b, c, 17);
|
||||
R1(c, d, e, a, b, 18);
|
||||
R1(b, c, d, e, a, 19);
|
||||
R2(a, b, c, d, e, 20);
|
||||
R2(e, a, b, c, d, 21);
|
||||
R2(d, e, a, b, c, 22);
|
||||
R2(c, d, e, a, b, 23);
|
||||
R2(b, c, d, e, a, 24);
|
||||
R2(a, b, c, d, e, 25);
|
||||
R2(e, a, b, c, d, 26);
|
||||
R2(d, e, a, b, c, 27);
|
||||
R2(c, d, e, a, b, 28);
|
||||
R2(b, c, d, e, a, 29);
|
||||
R2(a, b, c, d, e, 30);
|
||||
R2(e, a, b, c, d, 31);
|
||||
R2(d, e, a, b, c, 32);
|
||||
R2(c, d, e, a, b, 33);
|
||||
R2(b, c, d, e, a, 34);
|
||||
R2(a, b, c, d, e, 35);
|
||||
R2(e, a, b, c, d, 36);
|
||||
R2(d, e, a, b, c, 37);
|
||||
R2(c, d, e, a, b, 38);
|
||||
R2(b, c, d, e, a, 39);
|
||||
R3(a, b, c, d, e, 40);
|
||||
R3(e, a, b, c, d, 41);
|
||||
R3(d, e, a, b, c, 42);
|
||||
R3(c, d, e, a, b, 43);
|
||||
R3(b, c, d, e, a, 44);
|
||||
R3(a, b, c, d, e, 45);
|
||||
R3(e, a, b, c, d, 46);
|
||||
R3(d, e, a, b, c, 47);
|
||||
R3(c, d, e, a, b, 48);
|
||||
R3(b, c, d, e, a, 49);
|
||||
R3(a, b, c, d, e, 50);
|
||||
R3(e, a, b, c, d, 51);
|
||||
R3(d, e, a, b, c, 52);
|
||||
R3(c, d, e, a, b, 53);
|
||||
R3(b, c, d, e, a, 54);
|
||||
R3(a, b, c, d, e, 55);
|
||||
R3(e, a, b, c, d, 56);
|
||||
R3(d, e, a, b, c, 57);
|
||||
R3(c, d, e, a, b, 58);
|
||||
R3(b, c, d, e, a, 59);
|
||||
R4(a, b, c, d, e, 60);
|
||||
R4(e, a, b, c, d, 61);
|
||||
R4(d, e, a, b, c, 62);
|
||||
R4(c, d, e, a, b, 63);
|
||||
R4(b, c, d, e, a, 64);
|
||||
R4(a, b, c, d, e, 65);
|
||||
R4(e, a, b, c, d, 66);
|
||||
R4(d, e, a, b, c, 67);
|
||||
R4(c, d, e, a, b, 68);
|
||||
R4(b, c, d, e, a, 69);
|
||||
R4(a, b, c, d, e, 70);
|
||||
R4(e, a, b, c, d, 71);
|
||||
R4(d, e, a, b, c, 72);
|
||||
R4(c, d, e, a, b, 73);
|
||||
R4(b, c, d, e, a, 74);
|
||||
R4(a, b, c, d, e, 75);
|
||||
R4(e, a, b, c, d, 76);
|
||||
R4(d, e, a, b, c, 77);
|
||||
R4(c, d, e, a, b, 78);
|
||||
R4(b, c, d, e, a, 79);
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
#ifdef SHA1HANDSOFF
|
||||
memset(block, '\0', sizeof(block));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
|
||||
void SHA1Init(
|
||||
SHA1_CTX * context
|
||||
)
|
||||
{
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Run your data through this. */
|
||||
|
||||
void SHA1Update(
|
||||
SHA1_CTX * context,
|
||||
const unsigned char *data,
|
||||
uint32_t len
|
||||
)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
uint32_t j;
|
||||
|
||||
j = context->count[0];
|
||||
if ((context->count[0] += len << 3) < j)
|
||||
context->count[1]++;
|
||||
context->count[1] += (len >> 29);
|
||||
j = (j >> 3) & 63;
|
||||
if ((j + len) > 63)
|
||||
{
|
||||
memcpy(&context->buffer[j], data, (i = 64 - j));
|
||||
SHA1Transform(context->state, context->buffer);
|
||||
for (; i + 63 < len; i += 64)
|
||||
{
|
||||
SHA1Transform(context->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else
|
||||
i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
|
||||
void SHA1Final(
|
||||
unsigned char digest[20],
|
||||
SHA1_CTX * context
|
||||
)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
unsigned char finalcount[8];
|
||||
|
||||
unsigned char c;
|
||||
|
||||
#if 0 /* untested "improvement" by DHR */
|
||||
/* Convert context->count to a sequence of bytes
|
||||
* in finalcount. Second element first, but
|
||||
* big-endian order within element.
|
||||
* But we do it all backwards.
|
||||
*/
|
||||
unsigned char *fcp = &finalcount[8];
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
uint32_t t = context->count[i];
|
||||
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 4; t >>= 8, j++)
|
||||
*--fcp = (unsigned char) t}
|
||||
#else
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */
|
||||
}
|
||||
#endif
|
||||
c = 0200;
|
||||
SHA1Update(context, &c, 1);
|
||||
while ((context->count[0] & 504) != 448)
|
||||
{
|
||||
c = 0000;
|
||||
SHA1Update(context, &c, 1);
|
||||
}
|
||||
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
|
||||
for (i = 0; i < 20; i++)
|
||||
{
|
||||
digest[i] = (unsigned char)
|
||||
((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
|
||||
}
|
||||
/* Wipe variables */
|
||||
memset(context, '\0', sizeof(*context));
|
||||
memset(&finalcount, '\0', sizeof(finalcount));
|
||||
}
|
||||
|
||||
void SHA1(
|
||||
char *hash_out,
|
||||
const char *str,
|
||||
int len)
|
||||
{
|
||||
SHA1_CTX ctx;
|
||||
unsigned int ii;
|
||||
|
||||
SHA1Init(&ctx);
|
||||
for (ii=0; ii<len; ii+=1)
|
||||
SHA1Update(&ctx, (const unsigned char*)str + ii, 1);
|
||||
SHA1Final((unsigned char *)hash_out, &ctx);
|
||||
hash_out[20] = '\0';
|
||||
}
|
||||
44
jni/magiskboot/sha1.h
Normal file
44
jni/magiskboot/sha1.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef SHA1_H
|
||||
#define SHA1_H
|
||||
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
*/
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
unsigned char buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
void SHA1Transform(
|
||||
uint32_t state[5],
|
||||
const unsigned char buffer[64]
|
||||
);
|
||||
|
||||
void SHA1Init(
|
||||
SHA1_CTX * context
|
||||
);
|
||||
|
||||
void SHA1Update(
|
||||
SHA1_CTX * context,
|
||||
const unsigned char *data,
|
||||
uint32_t len
|
||||
);
|
||||
|
||||
void SHA1Final(
|
||||
unsigned char digest[20],
|
||||
SHA1_CTX * context
|
||||
);
|
||||
|
||||
void SHA1(
|
||||
char *hash_out,
|
||||
const char *str,
|
||||
int len);
|
||||
|
||||
#endif /* SHA1_H */
|
||||
@@ -1,8 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magiskhide
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_SRC_FILES := main.c hide.c list_monitor.c proc_monitor.c util.c
|
||||
LOCAL_CFLAGS += -std=gnu11 -O3
|
||||
include $(BUILD_EXECUTABLE)
|
||||
@@ -1,77 +0,0 @@
|
||||
#include "magiskhide.h"
|
||||
|
||||
int hideMagisk() {
|
||||
close(pipefd[1]);
|
||||
|
||||
int pid, fd;
|
||||
char cache_block[256];
|
||||
cache_block[0] = '\0';
|
||||
|
||||
while(1) {
|
||||
read(pipefd[0], &pid, sizeof(pid));
|
||||
// Termination called
|
||||
if(pid == -1) break;
|
||||
|
||||
manage_selinux();
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "/proc/%d/ns/mnt", pid);
|
||||
if((fd = open(buffer, O_RDONLY)) == -1) continue; // Maybe process died..
|
||||
if(setns(fd, 0) == -1) {
|
||||
fprintf(logfile, "MagiskHide: Unable to change namespace for pid=%d\n", pid);
|
||||
continue;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid);
|
||||
FILE *mount_fp = fopen(buffer, "r");
|
||||
if (mount_fp == NULL) {
|
||||
fprintf(logfile, "MagiskHide: Error opening mount list!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
int mount_size;
|
||||
char **mount_list = file_to_str_arr(mount_fp, &mount_size);
|
||||
|
||||
// Find the cache block name if not found yet
|
||||
if (strlen(cache_block) == 0) {
|
||||
for(i = 0; i < mount_size; ++i) {
|
||||
if (strstr(mount_list[i], " /cache ")) {
|
||||
sscanf(mount_list[i], "%256s", cache_block);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// First unmount the dummy skeletons and the cache mounts
|
||||
for(i = mount_size - 1; i >= 0; --i) {
|
||||
if (strstr(mount_list[i], "tmpfs /system") || strstr(mount_list[i], "tmpfs /vendor")
|
||||
|| (strstr(mount_list[i], cache_block) && strstr(mount_list[i], "/system")) ) {
|
||||
sscanf(mount_list[i], "%*s %512s", buffer);
|
||||
lazy_unmount(buffer);
|
||||
}
|
||||
free(mount_list[i]);
|
||||
}
|
||||
free(mount_list);
|
||||
|
||||
// Re-read mount infos
|
||||
fseek(mount_fp, 0, SEEK_SET);
|
||||
mount_list = file_to_str_arr(mount_fp, &mount_size);
|
||||
fclose(mount_fp);
|
||||
|
||||
// Unmount loop mounts
|
||||
for(i = mount_size - 1; i >= 0; --i) {
|
||||
if (strstr(mount_list[i], "/dev/block/loop") && !strstr(mount_list[i], DUMMYPATH)) {
|
||||
sscanf(mount_list[i], "%*s %512s", buffer);
|
||||
lazy_unmount(buffer);
|
||||
}
|
||||
free(mount_list[i]);
|
||||
}
|
||||
free(mount_list);
|
||||
|
||||
// Send resume signal
|
||||
kill(pid, SIGCONT);
|
||||
}
|
||||
|
||||
// Should never go here
|
||||
return 1;
|
||||
}
|
||||
243
jni/magiskhide/hide_utils.c
Normal file
243
jni/magiskhide/hide_utils.c
Normal file
@@ -0,0 +1,243 @@
|
||||
/* hide_utils.c - Some utility functions for MagiskHide
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "resetprop.h"
|
||||
#include "magiskhide.h"
|
||||
#include "daemon.h"
|
||||
|
||||
static char *prop_key[] =
|
||||
{ "ro.boot.verifiedbootstate", "ro.boot.flash.locked", "ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit",
|
||||
"ro.debuggable", "ro.secure", "ro.build.type", "ro.build.tags", "ro.build.selinux", NULL };
|
||||
|
||||
static char *prop_value[] =
|
||||
{ "green", "1", "enforcing", "0", "0", "0", "1", "user", "release-keys", "0", NULL };
|
||||
|
||||
static int mocked = 0;
|
||||
|
||||
void manage_selinux() {
|
||||
if (mocked) return;
|
||||
char val[1];
|
||||
int fd = xopen(SELINUX_ENFORCE, O_RDONLY);
|
||||
xxread(fd, val, 1);
|
||||
close(fd);
|
||||
// Permissive
|
||||
if (val[0] == '0') {
|
||||
LOGI("hide_daemon: Permissive detected, hide the state\n");
|
||||
|
||||
chmod(SELINUX_ENFORCE, 0640);
|
||||
chmod(SELINUX_POLICY, 0440);
|
||||
mocked = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void hide_sensitive_props() {
|
||||
LOGI("hide_utils: Hiding sensitive props\n");
|
||||
|
||||
// Hide all sensitive props
|
||||
char *value;
|
||||
for (int i = 0; prop_key[i]; ++i) {
|
||||
value = getprop(prop_key[i]);
|
||||
if (value) {
|
||||
if (strcmp(value, prop_value[i]) != 0)
|
||||
setprop2(prop_key[i], prop_value[i], 0);
|
||||
free(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rm_magisk_prop(const char *name) {
|
||||
if (strstr(name, "magisk")) {
|
||||
deleteprop(name, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void clean_magisk_props() {
|
||||
LOGD("hide_utils: Cleaning magisk props\n");
|
||||
getprop_all(rm_magisk_prop);
|
||||
}
|
||||
|
||||
void relink_sbin() {
|
||||
struct stat st;
|
||||
if (stat("/sbin_orig", &st) == -1 && errno == ENOENT) {
|
||||
// Re-link all binaries and bind mount
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
char from[PATH_MAX], to[PATH_MAX];
|
||||
|
||||
LOGI("hide_utils: Re-linking /sbin\n");
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xrename("/sbin", "/sbin_orig");
|
||||
xmkdir("/sbin", 0755);
|
||||
xchmod("/sbin", 0755);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
xmkdir("/dev/sbin_bind", 0755);
|
||||
xchmod("/dev/sbin_bind", 0755);
|
||||
dir = xopendir("/sbin_orig");
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
snprintf(from, sizeof(from), "/sbin_orig/%s", entry->d_name);
|
||||
if (entry->d_type == DT_LNK)
|
||||
xreadlink(from, from, sizeof(from));
|
||||
snprintf(to, sizeof(to), "/dev/sbin_bind/%s", entry->d_name);
|
||||
symlink(from, to);
|
||||
lsetfilecon(to, "u:object_r:rootfs:s0");
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
xmount("/dev/sbin_bind", "/sbin", NULL, MS_BIND, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int add_list(char *proc) {
|
||||
if (!hideEnabled) {
|
||||
free(proc);
|
||||
return HIDE_NOT_ENABLED;
|
||||
}
|
||||
|
||||
char *line;
|
||||
struct vector *new_list = xmalloc(sizeof(*new_list));
|
||||
if (new_list == NULL)
|
||||
return DAEMON_ERROR;
|
||||
vec_init(new_list);
|
||||
|
||||
vec_for_each(hide_list, line) {
|
||||
// They should be unique
|
||||
if (strcmp(line, proc) == 0) {
|
||||
free(proc);
|
||||
vec_destroy(new_list);
|
||||
free(new_list);
|
||||
return HIDE_ITEM_EXIST;
|
||||
}
|
||||
vec_push_back(new_list, line);
|
||||
}
|
||||
|
||||
vec_push_back(new_list, proc);
|
||||
LOGI("hide_list add: [%s]\n", proc);
|
||||
ps_filter_proc_name(proc, kill_proc);
|
||||
|
||||
// Critical region
|
||||
pthread_mutex_lock(&hide_lock);
|
||||
vec_destroy(hide_list);
|
||||
free(hide_list);
|
||||
hide_list = new_list;
|
||||
pthread_mutex_unlock(&hide_lock);
|
||||
|
||||
pthread_mutex_lock(&file_lock);
|
||||
if (vector_to_file(HIDELIST, hide_list)) {
|
||||
pthread_mutex_unlock(&file_lock);
|
||||
return DAEMON_ERROR;
|
||||
}
|
||||
pthread_mutex_unlock(&file_lock);
|
||||
return DAEMON_SUCCESS;
|
||||
}
|
||||
|
||||
int rm_list(char *proc) {
|
||||
if (!hideEnabled) {
|
||||
free(proc);
|
||||
return HIDE_NOT_ENABLED;
|
||||
}
|
||||
|
||||
daemon_response ret = DAEMON_ERROR;
|
||||
char *line;
|
||||
int do_rm = 0;
|
||||
struct vector *new_list = xmalloc(sizeof(*new_list));
|
||||
if (new_list == NULL)
|
||||
goto error;
|
||||
vec_init(new_list);
|
||||
|
||||
vec_for_each(hide_list, line) {
|
||||
if (strcmp(line, proc) == 0) {
|
||||
free(proc);
|
||||
proc = line;
|
||||
do_rm = 1;
|
||||
continue;
|
||||
}
|
||||
vec_push_back(new_list, line);
|
||||
}
|
||||
|
||||
if (do_rm) {
|
||||
LOGI("hide_list rm: [%s]\n", proc);
|
||||
ps_filter_proc_name(proc, kill_proc);
|
||||
// Critical region
|
||||
pthread_mutex_lock(&hide_lock);
|
||||
vec_destroy(hide_list);
|
||||
free(hide_list);
|
||||
hide_list = new_list;
|
||||
pthread_mutex_unlock(&hide_lock);
|
||||
|
||||
ret = DAEMON_SUCCESS;
|
||||
pthread_mutex_lock(&file_lock);
|
||||
if (vector_to_file(HIDELIST, hide_list))
|
||||
ret = DAEMON_ERROR;
|
||||
pthread_mutex_unlock(&file_lock);
|
||||
} else {
|
||||
ret = HIDE_ITEM_NOT_EXIST;
|
||||
vec_destroy(new_list);
|
||||
free(new_list);
|
||||
}
|
||||
|
||||
error:
|
||||
free(proc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int init_list() {
|
||||
LOGD("hide_list: initialize...\n");
|
||||
if ((hide_list = xmalloc(sizeof(*hide_list))) == NULL)
|
||||
return 1;
|
||||
vec_init(hide_list);
|
||||
|
||||
// Might error if file doesn't exist, no need to report
|
||||
file_to_vector(HIDELIST, hide_list);
|
||||
|
||||
char *line;
|
||||
vec_for_each(hide_list, line) {
|
||||
LOGI("hide_list: [%s]\n", line);
|
||||
ps_filter_proc_name(line, kill_proc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int destroy_list() {
|
||||
char *line;
|
||||
vec_for_each(hide_list, line) {
|
||||
ps_filter_proc_name(line, kill_proc);
|
||||
}
|
||||
vec_deep_destroy(hide_list);
|
||||
free(hide_list);
|
||||
hide_list = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void add_hide_list(int client) {
|
||||
err_handler = do_nothing;
|
||||
char *proc = read_string(client);
|
||||
// ack
|
||||
write_int(client, add_list(proc));
|
||||
close(client);
|
||||
}
|
||||
|
||||
void rm_hide_list(int client) {
|
||||
err_handler = do_nothing;
|
||||
char *proc = read_string(client);
|
||||
// ack
|
||||
write_int(client, rm_list(proc));
|
||||
close(client);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
#include "magiskhide.h"
|
||||
|
||||
void *monitor_list(void *path) {
|
||||
char* listpath = (char*) path;
|
||||
signal(SIGQUIT, quit_pthread);
|
||||
|
||||
int inotifyFd = -1;
|
||||
char str[512];
|
||||
|
||||
while(1) {
|
||||
if (inotifyFd == -1 || read(inotifyFd, str, sizeof(str)) == -1) {
|
||||
close(inotifyFd);
|
||||
inotifyFd = inotify_init();
|
||||
if (inotifyFd == -1) {
|
||||
fprintf(logfile, "MagiskHide: Unable to watch %s\n", listpath);
|
||||
exit(1);
|
||||
}
|
||||
if (inotify_add_watch(inotifyFd, listpath, IN_MODIFY) == -1) {
|
||||
fprintf(logfile, "MagiskHide: Unable to watch %s\n", listpath);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
update_list(listpath);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void update_list(const char *listpath) {
|
||||
FILE *hide_fp = fopen(listpath, "r");
|
||||
if (hide_fp == NULL) {
|
||||
fprintf(logfile, "MagiskHide: Error opening hide list\n");
|
||||
exit(1);
|
||||
}
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (hide_list) {
|
||||
// Free memory
|
||||
for(i = 0; i < list_size; ++i)
|
||||
free(hide_list[i]);
|
||||
free(hide_list);
|
||||
}
|
||||
hide_list = file_to_str_arr(hide_fp, &list_size);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
fclose(hide_fp);
|
||||
if (list_size) fprintf(logfile, "MagiskHide: Update process/package list:\n");
|
||||
for(i = 0; i < list_size; i++)
|
||||
fprintf(logfile, "MagiskHide: [%s]\n", hide_list[i]);
|
||||
}
|
||||
|
||||
void quit_pthread(int sig) {
|
||||
// Free memory
|
||||
for(i = 0; i < list_size; ++i)
|
||||
free(hide_list[i]);
|
||||
free(hide_list);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
166
jni/magiskhide/magiskhide.c
Normal file
166
jni/magiskhide/magiskhide.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/* magiskhide.c - initialize the environment for Magiskhide
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "magiskhide.h"
|
||||
#include "daemon.h"
|
||||
#include "resetprop.h"
|
||||
|
||||
struct vector *hide_list = NULL;
|
||||
|
||||
int hideEnabled = 0;
|
||||
static pthread_t proc_monitor_thread;
|
||||
pthread_mutex_t hide_lock, file_lock;
|
||||
|
||||
void kill_proc(int pid) {
|
||||
kill(pid, SIGTERM);
|
||||
}
|
||||
|
||||
static void usage(char *arg0) {
|
||||
fprintf(stderr,
|
||||
"MagiskHide v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - Hide Magisk!\n\n"
|
||||
"%s [--options [arguments...] ]\n\n"
|
||||
"Options:\n"
|
||||
" --enable: Start the magiskhide daemon\n"
|
||||
" --disable: Stop the magiskhide daemon\n"
|
||||
" --add <process name>: Add <process name> to the list\n"
|
||||
" --rm <process name>: Remove <process name> from the list\n"
|
||||
" --ls: Print out the current hide list\n"
|
||||
, arg0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void launch_magiskhide(int client) {
|
||||
// We manually handle crashes
|
||||
err_handler = do_nothing;
|
||||
|
||||
if (hideEnabled) {
|
||||
if (client > 0) {
|
||||
write_int(client, HIDE_IS_ENABLED);
|
||||
close(client);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
hideEnabled = 1;
|
||||
LOGI("* Starting MagiskHide\n");
|
||||
|
||||
deleteprop(MAGISKHIDE_PROP, 1);
|
||||
|
||||
hide_sensitive_props();
|
||||
|
||||
// Initialize the mutex lock
|
||||
pthread_mutex_init(&hide_lock, NULL);
|
||||
pthread_mutex_init(&file_lock, NULL);
|
||||
|
||||
// Initialize the hide list
|
||||
if (init_list())
|
||||
goto error;
|
||||
|
||||
// Add SafetyNet by default
|
||||
add_list(strdup("com.google.android.gms.unstable"));
|
||||
|
||||
if (client > 0) {
|
||||
write_int(client, DAEMON_SUCCESS);
|
||||
close(client);
|
||||
}
|
||||
|
||||
// Get thread reference
|
||||
proc_monitor_thread = pthread_self();
|
||||
// Start monitoring
|
||||
proc_monitor();
|
||||
return;
|
||||
|
||||
error:
|
||||
hideEnabled = 0;
|
||||
if (client > 0) {
|
||||
write_int(client, DAEMON_ERROR);
|
||||
close(client);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void stop_magiskhide(int client) {
|
||||
if (!hideEnabled) {
|
||||
write_int(client, HIDE_NOT_ENABLED);
|
||||
close(client);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGI("* Stopping MagiskHide\n");
|
||||
|
||||
hideEnabled = 0;
|
||||
setprop(MAGISKHIDE_PROP, "0");
|
||||
// Remove without actually removing persist props
|
||||
deleteprop(MAGISKHIDE_PROP, 0);
|
||||
pthread_kill(proc_monitor_thread, SIGUSR1);
|
||||
|
||||
write_int(client, DAEMON_SUCCESS);
|
||||
close(client);
|
||||
}
|
||||
|
||||
int magiskhide_main(int argc, char *argv[]) {
|
||||
if (argc < 2) {
|
||||
usage(argv[0]);
|
||||
}
|
||||
client_request req = DO_NOTHING;
|
||||
if (strcmp(argv[1], "--enable") == 0) {
|
||||
req = LAUNCH_MAGISKHIDE;
|
||||
} else if (strcmp(argv[1], "--disable") == 0) {
|
||||
req = STOP_MAGISKHIDE;
|
||||
} else if (strcmp(argv[1], "--add") == 0 && argc > 2) {
|
||||
req = ADD_HIDELIST;
|
||||
} else if (strcmp(argv[1], "--rm") == 0 && argc > 2) {
|
||||
req = RM_HIDELIST;
|
||||
} else if (strcmp(argv[1], "--ls") == 0) {
|
||||
FILE *fp = fopen(HIDELIST, "r");
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
char buffer[512];
|
||||
while (fgets(buffer, sizeof(buffer), fp)) {
|
||||
printf("%s", buffer);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, req);
|
||||
if (req == ADD_HIDELIST || req == RM_HIDELIST) {
|
||||
write_string(fd, argv[2]);
|
||||
}
|
||||
daemon_response code = read_int(fd);
|
||||
close(fd);
|
||||
switch (code) {
|
||||
case DAEMON_ERROR:
|
||||
fprintf(stderr, "Error occured in daemon...\n");
|
||||
break;
|
||||
case DAEMON_SUCCESS:
|
||||
break;
|
||||
case ROOT_REQUIRED:
|
||||
fprintf(stderr, "Root is required for this operation\n");
|
||||
break;
|
||||
case HIDE_NOT_ENABLED:
|
||||
fprintf(stderr, "Magisk hide is not enabled yet\n");
|
||||
break;
|
||||
case HIDE_IS_ENABLED:
|
||||
fprintf(stderr, "Magisk hide is already enabled\n");
|
||||
break;
|
||||
case HIDE_ITEM_EXIST:
|
||||
fprintf(stderr, "Process [%s] already exists in hide list\n", argv[2]);
|
||||
break;
|
||||
case HIDE_ITEM_NOT_EXIST:
|
||||
fprintf(stderr, "Process [%s] does not exist in hide list\n", argv[2]);
|
||||
break;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
@@ -1,52 +1,28 @@
|
||||
#ifndef MAGISK_HIDE_H
|
||||
#define MAGISK_HIDE_H
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sched.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <sys/mount.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/resource.h>
|
||||
// Kill process
|
||||
void kill_proc(int pid);
|
||||
|
||||
#define LOGFILE "/cache/magisk.log"
|
||||
#define HIDELIST "/magisk/.core/magiskhide/hidelist"
|
||||
#define DUMMYPATH "/dev/magisk/dummy"
|
||||
#define ENFORCE_FILE "/sys/fs/selinux/enforce"
|
||||
#define SEPOLICY_INJECT "/data/magisk/sepolicy-inject"
|
||||
// Process monitor
|
||||
void proc_monitor();
|
||||
|
||||
// Main thread
|
||||
void monitor_proc();
|
||||
|
||||
// Forked process for namespace setting
|
||||
int hideMagisk();
|
||||
|
||||
// List monitor thread
|
||||
void update_list(const char *listpath);
|
||||
void quit_pthread(int sig);
|
||||
void *monitor_list(void *path);
|
||||
|
||||
// Util functions
|
||||
char **file_to_str_arr(FILE *fp, int *size);
|
||||
void read_namespace(const int pid, char* target, const size_t size);
|
||||
void lazy_unmount(const char* mountpoint);
|
||||
void run_as_daemon();
|
||||
// Utility functions
|
||||
void manage_selinux();
|
||||
void hide_sensitive_props();
|
||||
void relink_sbin();
|
||||
void clean_magisk_props();
|
||||
|
||||
// Global variable sharing through process/threads
|
||||
extern FILE *logfile;
|
||||
extern int i, list_size, pipefd[2];
|
||||
extern char **hide_list, buffer[512];
|
||||
extern pthread_t list_monitor;
|
||||
extern pthread_mutex_t mutex;
|
||||
// List managements
|
||||
int add_list(char *proc);
|
||||
int rm_list(char *proc);
|
||||
int init_list();
|
||||
int destroy_list();
|
||||
|
||||
extern int hideEnabled;
|
||||
extern struct vector *hide_list;
|
||||
extern pthread_mutex_t hide_lock, file_lock;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#include "magiskhide.h"
|
||||
|
||||
FILE *logfile;
|
||||
int i, list_size, pipefd[2];
|
||||
char **hide_list = NULL, buffer[512];
|
||||
pthread_t list_monitor;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
static void terminate(int sig) {
|
||||
// Close the config list monitor
|
||||
pthread_kill(list_monitor, SIGQUIT);
|
||||
pthread_mutex_destroy(&mutex);
|
||||
|
||||
// Terminate our children
|
||||
i = -1;
|
||||
write(pipefd[1], &i, sizeof(i));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
if (argc > 1) {
|
||||
if (strcmp(argv[1], "--daemon") == 0)
|
||||
run_as_daemon();
|
||||
else {
|
||||
fprintf(stderr, "%s (with no options)\n\tRun magiskhide and output to stdout\n", argv[0]);
|
||||
fprintf(stderr, "%s --daemon\n\tRun magiskhide as daemon, output to magisk.log\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
} else
|
||||
logfile = stdout;
|
||||
|
||||
|
||||
// Handle all killing signals
|
||||
signal(SIGINT, terminate);
|
||||
signal(SIGTERM, terminate);
|
||||
|
||||
// Fork a child to handle namespace switches and unmounts
|
||||
pipe(pipefd);
|
||||
switch(fork()) {
|
||||
case -1:
|
||||
exit(-1);
|
||||
case 0:
|
||||
return hideMagisk();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
close(pipefd[0]);
|
||||
|
||||
// Start a thread to constantly check the hide list
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
pthread_create(&list_monitor, NULL, monitor_list, HIDELIST);
|
||||
|
||||
// Set main process to the top priority
|
||||
setpriority(PRIO_PROCESS, 0, -20);
|
||||
|
||||
monitor_proc();
|
||||
|
||||
terminate(0);
|
||||
|
||||
fprintf(logfile, "MagiskHide: Cannot monitor am_proc_start, abort...\n");
|
||||
fclose(logfile);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1,82 +1,277 @@
|
||||
/* proc_monitor.c - Monitor am_proc_start events and unmount
|
||||
*
|
||||
* We monitor the logcat am_proc_start events. When a target starts up,
|
||||
* we pause it ASAP, and fork a new process to join its mount namespace
|
||||
* and do all the unmounting/mocking
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
#include "magiskhide.h"
|
||||
|
||||
void monitor_proc() {
|
||||
int pid, badns, zygote_num = 0;
|
||||
char init_ns[32], zygote_ns[2][32];
|
||||
static int zygote_num;
|
||||
static char init_ns[32], zygote_ns[2][32], cache_block[256];
|
||||
static int log_pid, log_fd, target_pid;
|
||||
static char *buffer;
|
||||
|
||||
// Get the mount namespace of init
|
||||
read_namespace(1, init_ns, 32);
|
||||
|
||||
printf("%s\n", init_ns);
|
||||
|
||||
// Get the mount namespace of zygote
|
||||
FILE *p = popen("ps | grep zygote | grep -v grep", "r");
|
||||
while(fgets(buffer, sizeof(buffer), p)) {
|
||||
if (zygote_num == 2) break;
|
||||
sscanf(buffer, "%d", &pid);
|
||||
do {
|
||||
usleep(500);
|
||||
read_namespace(pid, zygote_ns[zygote_num], 32);
|
||||
} while (strcmp(zygote_ns[zygote_num], init_ns) == 0);
|
||||
++zygote_num;
|
||||
// Workaround for the lack of pthread_cancel
|
||||
static void quit_pthread(int sig) {
|
||||
err_handler = do_nothing;
|
||||
LOGD("proc_monitor: running cleanup\n");
|
||||
destroy_list();
|
||||
free(buffer);
|
||||
hideEnabled = 0;
|
||||
// Kill the logging if needed
|
||||
if (log_pid > 0) {
|
||||
kill(log_pid, SIGTERM);
|
||||
waitpid(log_pid, NULL, 0);
|
||||
close(log_fd);
|
||||
}
|
||||
pclose(p);
|
||||
// Resume process if possible
|
||||
if (target_pid > 0)
|
||||
kill(target_pid, SIGCONT);
|
||||
pthread_mutex_destroy(&hide_lock);
|
||||
pthread_mutex_destroy(&file_lock);
|
||||
LOGD("proc_monitor: terminating...\n");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < zygote_num; ++i)
|
||||
fprintf(logfile, "Zygote(%d) ns=%s ", i, zygote_ns[i]);
|
||||
fprintf(logfile, "\n");
|
||||
static void proc_monitor_err() {
|
||||
LOGD("proc_monitor: error occured, stopping magiskhide services\n");
|
||||
quit_pthread(SIGUSR1);
|
||||
}
|
||||
|
||||
// Monitor am_proc_start
|
||||
p = popen("while true; do logcat -b events -c; logcat -b events -v raw -s am_proc_start; sleep 1; done", "r");
|
||||
static int read_namespace(const int pid, char* target, const size_t size) {
|
||||
char path[32];
|
||||
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
|
||||
if (access(path, R_OK) == -1)
|
||||
return 1;
|
||||
xreadlink(path, target, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while(!feof(p)) {
|
||||
//Format of am_proc_start is (as of Android 5.1 and 6.0)
|
||||
//UserID, pid, unix uid, processName, hostingType, hostingName
|
||||
fgets(buffer, sizeof(buffer), p);
|
||||
static void store_zygote_ns(int pid) {
|
||||
if (zygote_num == 2) return;
|
||||
do {
|
||||
usleep(500);
|
||||
read_namespace(pid, zygote_ns[zygote_num], 32);
|
||||
} while (strcmp(zygote_ns[zygote_num], init_ns) == 0);
|
||||
++zygote_num;
|
||||
}
|
||||
|
||||
char *pos = buffer;
|
||||
while(1) {
|
||||
pos = strchr(pos, ',');
|
||||
if(pos == NULL)
|
||||
break;
|
||||
pos[0] = ' ';
|
||||
}
|
||||
static void lazy_unmount(const char* mountpoint) {
|
||||
if (umount2(mountpoint, MNT_DETACH) != -1)
|
||||
LOGD("hide_daemon: Unmounted (%s)\n", mountpoint);
|
||||
else
|
||||
LOGD("hide_daemon: Unmount Failed (%s)\n", mountpoint);
|
||||
}
|
||||
|
||||
char processName[256];
|
||||
int ret = sscanf(buffer, "[%*d %d %*d %256s", &pid, processName);
|
||||
static void hide_daemon_err() {
|
||||
LOGD("hide_daemon: error occured, stopping magiskhide services\n");
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
if(ret != 2)
|
||||
continue;
|
||||
static void hide_daemon(int pid) {
|
||||
LOGD("hide_daemon: start unmount for pid=[%d]\n", pid);
|
||||
// When an error occurs, report its failure to main process
|
||||
err_handler = hide_daemon_err;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
for (i = 0; i < list_size; ++i) {
|
||||
if(strcmp(processName, hide_list[i]) == 0) {
|
||||
while(1) {
|
||||
badns = 0;
|
||||
read_namespace(pid, buffer, 32);
|
||||
for (i = 0; i < zygote_num; ++i) {
|
||||
if (strcmp(buffer, zygote_ns[i]) == 0) {
|
||||
usleep(500);
|
||||
badns = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!badns) break;
|
||||
}
|
||||
char *line;
|
||||
struct vector mount_list;
|
||||
|
||||
// Send pause signal ASAP
|
||||
if (kill(pid, SIGSTOP) == -1) continue;
|
||||
manage_selinux();
|
||||
relink_sbin();
|
||||
clean_magisk_props();
|
||||
|
||||
fprintf(logfile, "MagiskHide: %s(PID=%d ns=%s)\n", processName, pid, buffer);
|
||||
if (switch_mnt_ns(pid))
|
||||
return;
|
||||
|
||||
// Unmount start
|
||||
write(pipefd[1], &pid, sizeof(pid));
|
||||
snprintf(buffer, PATH_MAX, "/proc/%d/mounts", pid);
|
||||
vec_init(&mount_list);
|
||||
file_to_vector(buffer, &mount_list);
|
||||
|
||||
// Find the cache block name if not found yet
|
||||
if (cache_block[0] == '\0') {
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, " /cache ")) {
|
||||
sscanf(line, "%256s", cache_block);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
// Close the logcat monitor
|
||||
pclose(p);
|
||||
}
|
||||
// First unmount dummy skeletons, /sbin links, cache mounts, and mirrors
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, "tmpfs /system") || strstr(line, "tmpfs /vendor") || strstr(line, "tmpfs /sbin")
|
||||
|| (strstr(line, cache_block) && (strstr(line, " /system") || strstr(line, " /vendor")))
|
||||
|| strstr(line, MIRRDIR)) {
|
||||
sscanf(line, "%*s %4096s", buffer);
|
||||
lazy_unmount(buffer);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
vec_destroy(&mount_list);
|
||||
|
||||
// Re-read mount infos
|
||||
snprintf(buffer, PATH_MAX, "/proc/%d/mounts", pid);
|
||||
vec_init(&mount_list);
|
||||
file_to_vector(buffer, &mount_list);
|
||||
|
||||
// Unmount any loop mounts and dummy mounts
|
||||
vec_for_each(&mount_list, line) {
|
||||
if (strstr(line, "/dev/block/loop") || strstr(line, DUMMDIR)) {
|
||||
sscanf(line, "%*s %4096s", buffer);
|
||||
lazy_unmount(buffer);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
|
||||
// Free uo memory
|
||||
vec_destroy(&mount_list);
|
||||
}
|
||||
|
||||
void proc_monitor() {
|
||||
// Register the cancel signal
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = quit_pthread;
|
||||
sigaction(SIGUSR1, &act, NULL);
|
||||
|
||||
// The error handler should stop magiskhide services
|
||||
err_handler = proc_monitor_err;
|
||||
log_pid = target_pid = -1;
|
||||
|
||||
buffer = xmalloc(PATH_MAX);
|
||||
cache_block[0] = '\0';
|
||||
|
||||
// Get the mount namespace of init
|
||||
if (read_namespace(1, init_ns, 32)) {
|
||||
LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n");
|
||||
proc_monitor_err();
|
||||
}
|
||||
LOGI("proc_monitor: init ns=%s\n", init_ns);
|
||||
|
||||
// Get the mount namespace of zygote
|
||||
zygote_num = 0;
|
||||
while(!zygote_num) {
|
||||
// Check zygote every 2 secs
|
||||
sleep(2);
|
||||
ps_filter_proc_name("zygote", store_zygote_ns);
|
||||
}
|
||||
ps_filter_proc_name("zygote64", store_zygote_ns);
|
||||
|
||||
switch(zygote_num) {
|
||||
case 1:
|
||||
LOGI("proc_monitor: zygote ns=%s\n", zygote_ns[0]);
|
||||
break;
|
||||
case 2:
|
||||
LOGI("proc_monitor: zygote ns=%s zygote64 ns=%s\n", zygote_ns[0], zygote_ns[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
// Clear previous logcat buffer
|
||||
char *const restart[] = { "logcat", "-b", "events", "-c", NULL };
|
||||
log_pid = run_command(0, NULL, "/system/bin/logcat", restart);
|
||||
if (log_pid > 0)
|
||||
waitpid(log_pid, NULL, 0);
|
||||
|
||||
// Monitor am_proc_start
|
||||
char *const command[] = { "logcat", "-b", "events", "-v", "raw", "-s", "am_proc_start", NULL };
|
||||
log_fd = -1;
|
||||
log_pid = run_command(0, &log_fd, "/system/bin/logcat", command);
|
||||
|
||||
if (log_pid < 0) continue;
|
||||
if (kill(log_pid, 0)) continue;
|
||||
|
||||
while(fdgets(buffer, PATH_MAX, log_fd)) {
|
||||
int pid, ret, comma = 0;
|
||||
char *pos = buffer, *line, processName[256];
|
||||
|
||||
while(1) {
|
||||
pos = strchr(pos, ',');
|
||||
if(pos == NULL)
|
||||
break;
|
||||
pos[0] = ' ';
|
||||
++comma;
|
||||
}
|
||||
|
||||
if (comma == 6)
|
||||
ret = sscanf(buffer, "[%*d %d %*d %*d %256s", &pid, processName);
|
||||
else
|
||||
ret = sscanf(buffer, "[%*d %d %*d %256s", &pid, processName);
|
||||
|
||||
if(ret != 2)
|
||||
continue;
|
||||
|
||||
ret = 0;
|
||||
|
||||
// Critical region
|
||||
pthread_mutex_lock(&hide_lock);
|
||||
vec_for_each(hide_list, line) {
|
||||
if (strcmp(processName, line) == 0) {
|
||||
target_pid = pid;
|
||||
while(1) {
|
||||
ret = 1;
|
||||
for (int i = 0; i < zygote_num; ++i) {
|
||||
read_namespace(target_pid, buffer, 32);
|
||||
if (strcmp(buffer, zygote_ns[i]) == 0) {
|
||||
usleep(50);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret) break;
|
||||
}
|
||||
|
||||
// Send pause signal ASAP
|
||||
if (kill(target_pid, SIGSTOP) == -1) continue;
|
||||
|
||||
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, target_pid, buffer);
|
||||
|
||||
/*
|
||||
* The setns system call do not support multithread processes
|
||||
* We have to fork a new process, setns, then do the unmounts
|
||||
*/
|
||||
int hide_pid = fork();
|
||||
switch(hide_pid) {
|
||||
case -1:
|
||||
PLOGE("fork");
|
||||
return;
|
||||
case 0:
|
||||
hide_daemon(target_pid);
|
||||
_exit(0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Wait till the unmount process is done
|
||||
waitpid(hide_pid, &ret, 0);
|
||||
if (WEXITSTATUS(ret))
|
||||
quit_pthread(SIGUSR1);
|
||||
|
||||
// All done, send resume signal
|
||||
kill(target_pid, SIGCONT);
|
||||
target_pid = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&hide_lock);
|
||||
}
|
||||
|
||||
// For some reason it went here, restart logging
|
||||
kill(log_pid, SIGTERM);
|
||||
waitpid(log_pid, NULL, 0);
|
||||
close(log_fd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
#include "magiskhide.h"
|
||||
|
||||
char **file_to_str_arr(FILE *fp, int *size) {
|
||||
int allocated = 16;
|
||||
char *line = NULL, **array;
|
||||
size_t len = 0;
|
||||
ssize_t read;
|
||||
|
||||
array = (char **) malloc(sizeof(char*) * allocated);
|
||||
|
||||
*size = 0;
|
||||
while ((read = getline(&line, &len, fp)) != -1) {
|
||||
if (*size >= allocated) {
|
||||
// Double our allocation and re-allocate
|
||||
allocated *= 2;
|
||||
array = (char **) realloc(array, sizeof(char*) * allocated);
|
||||
}
|
||||
// Remove end newline
|
||||
if (line[read - 1] == '\n') {
|
||||
line[read - 1] = '\0';
|
||||
}
|
||||
array[*size] = line;
|
||||
line = NULL;
|
||||
++(*size);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
void read_namespace(const int pid, char* target, const size_t size) {
|
||||
char path[32];
|
||||
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
|
||||
ssize_t len = readlink(path, target, size);
|
||||
target[len] = '\0';
|
||||
}
|
||||
|
||||
void lazy_unmount(const char* mountpoint) {
|
||||
if (umount2(mountpoint, MNT_DETACH) != -1)
|
||||
fprintf(logfile, "MagiskHide: Unmounted (%s)\n", mountpoint);
|
||||
else
|
||||
fprintf(logfile, "MagiskHide: Unmount Failed (%s)\n", mountpoint);
|
||||
}
|
||||
|
||||
void run_as_daemon() {
|
||||
switch(fork()) {
|
||||
case -1:
|
||||
exit(-1);
|
||||
case 0:
|
||||
if (setsid() < 0)
|
||||
exit(-1);
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
logfile = fopen(LOGFILE, "a+");
|
||||
setbuf(logfile, NULL);
|
||||
break;
|
||||
default:
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void manage_selinux() {
|
||||
char *argv[] = { SEPOLICY_INJECT, "--live", "permissive *", NULL };
|
||||
char str[20];
|
||||
int fd, ret;
|
||||
fd = open(ENFORCE_FILE, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return;
|
||||
ret = read(fd, str, 20);
|
||||
close(fd);
|
||||
if (ret < 1)
|
||||
return;
|
||||
// Permissive
|
||||
if (str[0] == '0') {
|
||||
fprintf(logfile, "MagiskHide: Permissive detected, switching to pseudo enforced\n");
|
||||
fd = open(ENFORCE_FILE, O_RDWR);
|
||||
if (fd < 0)
|
||||
return;
|
||||
ret = write(fd, "1", 1);
|
||||
close(fd);
|
||||
if (ret < 1)
|
||||
return;
|
||||
switch(fork()) {
|
||||
case -1:
|
||||
return;
|
||||
case 0:
|
||||
execvp(argv[0], argv);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
jni/magiskpolicy
Submodule
1
jni/magiskpolicy
Submodule
Submodule jni/magiskpolicy added at c1c6f55f8f
163
jni/main.c
Normal file
163
jni/main.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/* main.c - The multicall entry point
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "magisk.h"
|
||||
#include "daemon.h"
|
||||
|
||||
char *argv0;
|
||||
|
||||
char *applet[] =
|
||||
{ "su", "resetprop", "magiskpolicy", "supolicy", "sepolicy-inject", "magiskhide", NULL };
|
||||
|
||||
int (*applet_main[]) (int, char *[]) =
|
||||
{ su_client_main, resetprop_main, magiskpolicy_main, magiskpolicy_main, magiskpolicy_main, magiskhide_main, NULL };
|
||||
|
||||
// Global error hander function
|
||||
// Should be changed each thread/process
|
||||
__thread void (*err_handler)(void);
|
||||
|
||||
static void usage() {
|
||||
fprintf(stderr,
|
||||
"Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n"
|
||||
"\n"
|
||||
"Usage: %s [applet [arguments]...]\n"
|
||||
" or: %s --install [SOURCE] <DIR> \n"
|
||||
" or: %s --list\n"
|
||||
" or: %s --createimg <PATH> <SIZE>\n"
|
||||
" create ext4 image, SIZE is interpreted in MB\n"
|
||||
" or: %s --imgsize <PATH>\n"
|
||||
" or: %s --resizeimg <PATH> <SIZE>\n"
|
||||
" SIZE is interpreted in MB\n"
|
||||
" or: %s --mountimg <IMG> <PATH>\n"
|
||||
" Prints out the loop device\n"
|
||||
" or: %s --umountimg <PATH> <LOOP>\n"
|
||||
" or: %s --[boot stage]\n"
|
||||
" start boot stage service\n"
|
||||
" or: %s [options]\n"
|
||||
" or: applet [arguments]...\n"
|
||||
"\n"
|
||||
"Supported boot stages:\n"
|
||||
" post-fs, post-fs-data, service\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -c print client version\n"
|
||||
" -v print daemon version\n"
|
||||
" -V print daemon version code\n"
|
||||
"\n"
|
||||
"Supported applets:\n"
|
||||
, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
|
||||
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
fprintf(stderr, i ? ", %s" : " %s", applet[i]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
argv0 = argv[0];
|
||||
// Exit the whole app if error occurs by default
|
||||
err_handler = exit_proc;
|
||||
char * arg = strrchr(argv[0], '/');
|
||||
if (arg) ++arg;
|
||||
else arg = argv[0];
|
||||
if (strcmp(arg, "magisk") == 0) {
|
||||
if (argc < 2) usage();
|
||||
if (strcmp(argv[1], "-c") == 0) {
|
||||
printf("%s\n", MAGISK_VER_STR);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "-v") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, CHECK_VERSION);
|
||||
char *v = read_string(fd);
|
||||
printf("%s\n", v);
|
||||
free(v);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "-V") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, CHECK_VERSION_CODE);
|
||||
printf("%d\n", read_int(fd));
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--install") == 0) {
|
||||
if (argc < 3) usage();
|
||||
if (argc == 3) return create_links(NULL, argv[2]);
|
||||
else return create_links(argv[2], argv[3]);
|
||||
} else if (strcmp(argv[1], "--list") == 0) {
|
||||
for (int i = 0; applet[i]; ++i)
|
||||
printf("%s\n", applet[i]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--createimg") == 0) {
|
||||
if (argc < 4) usage();
|
||||
int size;
|
||||
sscanf(argv[3], "%d", &size);
|
||||
return create_img(argv[2], size);
|
||||
} else if (strcmp(argv[1], "--imgsize") == 0) {
|
||||
if (argc < 3) usage();
|
||||
int used, total;
|
||||
if (get_img_size(argv[2], &used, &total)) {
|
||||
fprintf(stderr, "Cannot check %s size\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
printf("%d %d\n", used, total);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--resizeimg") == 0) {
|
||||
if (argc < 4) usage();
|
||||
int used, total, size;
|
||||
sscanf(argv[3], "%d", &size);
|
||||
if (get_img_size(argv[2], &used, &total)) {
|
||||
fprintf(stderr, "Cannot check %s size\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
if (size <= used) {
|
||||
fprintf(stderr, "Cannot resize smaller than %dM\n", used);
|
||||
return 1;
|
||||
}
|
||||
return resize_img(argv[2], size);
|
||||
} else if (strcmp(argv[1], "--mountimg") == 0) {
|
||||
if (argc < 4) usage();
|
||||
char *loop = mount_image(argv[2], argv[3]);
|
||||
if (loop == NULL) {
|
||||
fprintf(stderr, "Cannot mount image!\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("%s\n", loop);
|
||||
free(loop);
|
||||
return 0;
|
||||
}
|
||||
} else if (strcmp(argv[1], "--umountimg") == 0) {
|
||||
if (argc < 4) usage();
|
||||
umount_image(argv[2], argv[3]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--post-fs") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, POST_FS);
|
||||
return read_int(fd);
|
||||
} else if (strcmp(argv[1], "--post-fs-data") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, POST_FS_DATA);
|
||||
return read_int(fd);
|
||||
} else if (strcmp(argv[1], "--service") == 0) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, LATE_START);
|
||||
return read_int(fd);
|
||||
} else {
|
||||
// It's calling applets
|
||||
--argc;
|
||||
++argv;
|
||||
arg = argv[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Applets
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
if (strcmp(arg, applet[i]) == 0)
|
||||
return (*applet_main[i])(argc, argv);
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: applet not found\n", arg);
|
||||
return 1;
|
||||
}
|
||||
1
jni/ndk-compression
Submodule
1
jni/ndk-compression
Submodule
Submodule jni/ndk-compression added at 278ea80348
Submodule jni/resetprop deleted from 3a6db7dbaf
8
jni/resetprop/Android.mk
Normal file
8
jni/resetprop/Android.mk
Normal file
@@ -0,0 +1,8 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := resetprop
|
||||
LOCAL_SRC_FILES := resetprop.cpp system_properties.cpp libc_logging.cpp
|
||||
LOCAL_LDLIBS += -latomic
|
||||
LOCAL_CFLAGS := -Wno-implicit-exception-spec-mismatch -DINDEP_BINARY
|
||||
include $(BUILD_EXECUTABLE)
|
||||
43
jni/resetprop/ErrnoRestorer.h
Normal file
43
jni/resetprop/ErrnoRestorer.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ERRNO_RESTORER_H
|
||||
#define ERRNO_RESTORER_H
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "bionic_macros.h"
|
||||
|
||||
class ErrnoRestorer {
|
||||
public:
|
||||
explicit ErrnoRestorer() : saved_errno_(errno) {
|
||||
}
|
||||
|
||||
~ErrnoRestorer() {
|
||||
errno = saved_errno_;
|
||||
}
|
||||
|
||||
void override(int new_errno) {
|
||||
saved_errno_ = new_errno;
|
||||
}
|
||||
|
||||
private:
|
||||
int saved_errno_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
|
||||
};
|
||||
|
||||
#endif // ERRNO_RESTORER_H
|
||||
144
jni/resetprop/_system_properties.h
Normal file
144
jni/resetprop/_system_properties.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SYS__SYSTEM_PROPERTIES_H
|
||||
#define _INCLUDE_SYS__SYSTEM_PROPERTIES_H
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||
#error you should #include <sys/system_properties.h> instead
|
||||
#endif
|
||||
|
||||
// #include <sys/system_properties.h>
|
||||
#include "system_properties.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define PROP_SERVICE_NAME "property_service"
|
||||
#define PROP_FILENAME "/dev/__properties__"
|
||||
|
||||
#define PROP_MSG_SETPROP 1
|
||||
#define PROP_MSG_SETPROP2 0x00020001
|
||||
|
||||
#define PROP_SUCCESS 0
|
||||
#define PROP_ERROR_READ_CMD 0x0004
|
||||
#define PROP_ERROR_READ_DATA 0x0008
|
||||
#define PROP_ERROR_READ_ONLY_PROPERTY 0x000B
|
||||
#define PROP_ERROR_INVALID_NAME 0x0010
|
||||
#define PROP_ERROR_INVALID_VALUE 0x0014
|
||||
#define PROP_ERROR_PERMISSION_DENIED 0x0018
|
||||
#define PROP_ERROR_INVALID_CMD 0x001B
|
||||
#define PROP_ERROR_HANDLE_CONTROL_MESSAGE 0x0020
|
||||
#define PROP_ERROR_SET_FAILED 0x0024
|
||||
|
||||
/*
|
||||
** Map the property area from the specified filename. This
|
||||
** method is for testing only.
|
||||
*/
|
||||
int __system_property_set_filename2(const char *filename);
|
||||
|
||||
/*
|
||||
** Initialize the area to be used to store properties. Can
|
||||
** only be done by a single process that has write access to
|
||||
** the property area.
|
||||
*/
|
||||
int __system_property_area_init2();
|
||||
|
||||
/* Read the global serial number of the system properties
|
||||
**
|
||||
** Called to predict if a series of cached __system_property_find
|
||||
** objects will have seen __system_property_serial values change.
|
||||
** But also aids the converse, as changes in the global serial can
|
||||
** also be used to predict if a failed __system_property_find
|
||||
** could in-turn now find a new object; thus preventing the
|
||||
** cycles of effort to poll __system_property_find.
|
||||
**
|
||||
** Typically called at beginning of a cache cycle to signal if _any_ possible
|
||||
** changes have occurred since last. If there is, one may check each individual
|
||||
** __system_property_serial to confirm dirty, or __system_property_find
|
||||
** to check if the property now exists. If a call to __system_property_add
|
||||
** or __system_property_update has completed between two calls to
|
||||
** __system_property_area_serial then the second call will return a larger
|
||||
** value than the first call. Beware of race conditions as changes to the
|
||||
** properties are not atomic, the main value of this call is to determine
|
||||
** whether the expensive __system_property_find is worth retrying to see if
|
||||
** a property now exists.
|
||||
**
|
||||
** Returns the serial number on success, -1 on error.
|
||||
*/
|
||||
uint32_t __system_property_area_serial2();
|
||||
|
||||
/* Add a new system property. Can only be done by a single
|
||||
** process that has write access to the property area, and
|
||||
** that process must handle sequencing to ensure the property
|
||||
** does not already exist and that only one property is added
|
||||
** or updated at a time.
|
||||
**
|
||||
** Returns 0 on success, -1 if the property area is full.
|
||||
*/
|
||||
int __system_property_add2(const char *name, unsigned int namelen, const char *value, unsigned int valuelen);
|
||||
|
||||
/* Delete a new system property. Added in resetprop
|
||||
**
|
||||
** Returns 0 on success, -1 if the property area is full.
|
||||
*/
|
||||
int __system_property_del(const char *name);
|
||||
|
||||
/* Update the value of a system property returned by
|
||||
** __system_property_find. Can only be done by a single process
|
||||
** that has write access to the property area, and that process
|
||||
** must handle sequencing to ensure that only one property is
|
||||
** updated at a time.
|
||||
**
|
||||
** Returns 0 on success, -1 if the parameters are incorrect.
|
||||
*/
|
||||
int __system_property_update2(prop_info *pi, const char *value, unsigned int len);
|
||||
|
||||
/* Read the serial number of a system property returned by
|
||||
** __system_property_find.
|
||||
**
|
||||
** Returns the serial number on success, -1 on error.
|
||||
*/
|
||||
uint32_t __system_property_serial2(const prop_info* pi);
|
||||
|
||||
/* Initialize the system properties area in read only mode.
|
||||
* Should be done by all processes that need to read system
|
||||
* properties.
|
||||
*
|
||||
* Returns 0 on success, -1 otherwise.
|
||||
*/
|
||||
int __system_properties_init2();
|
||||
|
||||
/* Deprecated: use __system_property_wait instead. */
|
||||
uint32_t __system_property_wait_any2(uint32_t old_serial);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
77
jni/resetprop/bionic_futex.h
Normal file
77
jni/resetprop/bionic_futex.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _BIONIC_FUTEX_H
|
||||
#define _BIONIC_FUTEX_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <linux/futex.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
struct timespec;
|
||||
|
||||
static inline __always_inline int __futex(volatile void* ftx, int op, int value,
|
||||
const struct timespec* timeout,
|
||||
int bitset) {
|
||||
// Our generated syscall assembler sets errno, but our callers (pthread functions) don't want to.
|
||||
int saved_errno = errno;
|
||||
int result = syscall(__NR_futex, ftx, op, value, timeout, NULL, bitset);
|
||||
if (__predict_false(result == -1)) {
|
||||
result = -errno;
|
||||
errno = saved_errno;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int __futex_wake(volatile void* ftx, int count) {
|
||||
return __futex(ftx, FUTEX_WAKE, count, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int __futex_wake_ex(volatile void* ftx, bool shared, int count) {
|
||||
return __futex(ftx, shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE, count, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int __futex_wait(volatile void* ftx, int value, const struct timespec* timeout) {
|
||||
return __futex(ftx, FUTEX_WAIT, value, timeout, 0);
|
||||
}
|
||||
|
||||
static inline int __futex_wait_ex(volatile void* ftx, bool shared, int value,
|
||||
bool use_realtime_clock, const struct timespec* abs_timeout) {
|
||||
return __futex(ftx, (shared ? FUTEX_WAIT_BITSET : FUTEX_WAIT_BITSET_PRIVATE) |
|
||||
(use_realtime_clock ? FUTEX_CLOCK_REALTIME : 0), value, abs_timeout,
|
||||
FUTEX_BITSET_MATCH_ANY);
|
||||
}
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* _BIONIC_FUTEX_H */
|
||||
79
jni/resetprop/bionic_lock.h
Normal file
79
jni/resetprop/bionic_lock.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _BIONIC_LOCK_H
|
||||
#define _BIONIC_LOCK_H
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include "bionic_futex.h"
|
||||
#include "bionic_macros.h"
|
||||
|
||||
// Lock is used in places like pthread_rwlock_t, which can be initialized without calling
|
||||
// an initialization function. So make sure Lock can be initialized by setting its memory to 0.
|
||||
class Lock {
|
||||
private:
|
||||
enum LockState {
|
||||
Unlocked = 0,
|
||||
LockedWithoutWaiter,
|
||||
LockedWithWaiter,
|
||||
};
|
||||
_Atomic(LockState) state;
|
||||
bool process_shared;
|
||||
|
||||
public:
|
||||
void init(bool process_shared) {
|
||||
atomic_init(&state, Unlocked);
|
||||
this->process_shared = process_shared;
|
||||
}
|
||||
|
||||
bool trylock() {
|
||||
LockState old_state = Unlocked;
|
||||
return __predict_true(atomic_compare_exchange_strong_explicit(&state, &old_state,
|
||||
LockedWithoutWaiter, memory_order_acquire, memory_order_relaxed));
|
||||
}
|
||||
|
||||
void lock() {
|
||||
LockState old_state = Unlocked;
|
||||
if (__predict_true(atomic_compare_exchange_strong_explicit(&state, &old_state,
|
||||
LockedWithoutWaiter, memory_order_acquire, memory_order_relaxed))) {
|
||||
return;
|
||||
}
|
||||
while (atomic_exchange_explicit(&state, LockedWithWaiter, memory_order_acquire) != Unlocked) {
|
||||
// TODO: As the critical section is brief, it is a better choice to spin a few times befor sleeping.
|
||||
__futex_wait_ex(&state, process_shared, LockedWithWaiter, false, nullptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
if (atomic_exchange_explicit(&state, Unlocked, memory_order_release) == LockedWithWaiter) {
|
||||
__futex_wake_ex(&state, process_shared, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _BIONIC_LOCK_H
|
||||
69
jni/resetprop/bionic_macros.h
Normal file
69
jni/resetprop/bionic_macros.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _BIONIC_MACROS_H_
|
||||
#define _BIONIC_MACROS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Frameworks OpenGL code currently leaks this header and allows
|
||||
// collisions with other declarations, e.g., from libnativehelper.
|
||||
// TODO: Remove once cleaned up. b/18334516
|
||||
#if !defined(DISALLOW_COPY_AND_ASSIGN)
|
||||
// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
|
||||
// It goes in the private: declarations in a class.
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
void operator=(const TypeName&) = delete
|
||||
#endif // !defined(DISALLOW_COPY_AND_ASSIGN)
|
||||
|
||||
// A macro to disallow all the implicit constructors, namely the
|
||||
// default constructor, copy constructor and operator= functions.
|
||||
//
|
||||
// This should be used in the private: declarations for a class
|
||||
// that wants to prevent anyone from instantiating it. This is
|
||||
// especially useful for classes containing only static methods.
|
||||
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||
TypeName() = delete; \
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||
|
||||
#define BIONIC_ALIGN(value, alignment) \
|
||||
(((value) + (alignment) - 1) & ~((alignment) - 1))
|
||||
|
||||
#define BIONIC_ROUND_UP_POWER_OF_2(value) \
|
||||
((sizeof(value) == 8) \
|
||||
? (1UL << (64 - __builtin_clzl(static_cast<unsigned long>(value)))) \
|
||||
: (1UL << (32 - __builtin_clz(static_cast<unsigned int>(value)))))
|
||||
|
||||
static constexpr uintptr_t align_down(uintptr_t p, size_t align) {
|
||||
return p & ~(align - 1);
|
||||
}
|
||||
|
||||
static constexpr uintptr_t align_up(uintptr_t p, size_t align) {
|
||||
return (p + align - 1) & ~(align - 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* align_down(T* p, size_t align) {
|
||||
return reinterpret_cast<T*>(align_down(reinterpret_cast<uintptr_t>(p), align));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* align_up(T* p, size_t align) {
|
||||
return reinterpret_cast<T*>(align_up(reinterpret_cast<uintptr_t>(p), align));
|
||||
}
|
||||
|
||||
#endif // _BIONIC_MACROS_H_
|
||||
306
jni/resetprop/resetprop.cpp
Normal file
306
jni/resetprop/resetprop.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
/* resetprop.cpp - Manipulate any system props
|
||||
*
|
||||
* Copyright 2016 nkk71 <nkk71x@gmail.com>
|
||||
* Copyright 2016 topjohnwu <topjohnwu@gmail.com>
|
||||
*
|
||||
* Info:
|
||||
*
|
||||
* all changes are in
|
||||
*
|
||||
* bionic/libc/bionic/system_properties.cpp
|
||||
*
|
||||
* Functions that need to be patched/added in system_properties.cpp
|
||||
*
|
||||
* int __system_properties_init2()
|
||||
* on android 7, first tear down the everything then let it initialize again:
|
||||
* if (initialized) {
|
||||
* //list_foreach(contexts, [](context_node* l) { l->reset_access(); });
|
||||
* //return 0;
|
||||
* free_and_unmap_contexts();
|
||||
* initialized = false;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* static prop_area* map_prop_area(const char* filename, bool is_legacy)
|
||||
* we dont want this read only so change: 'O_RDONLY' to 'O_RDWR'
|
||||
*
|
||||
* static prop_area* map_fd_ro(const int fd)
|
||||
* we dont want this read only so change: 'PROT_READ' to 'PROT_READ | PROT_WRITE'
|
||||
*
|
||||
*
|
||||
* Copy the code of prop_info *prop_area::find_property, and modify to delete props
|
||||
* const prop_info *prop_area::find_property_and_del(prop_bt *const trie, const char *name)
|
||||
* {
|
||||
* ...
|
||||
* ... Do not alloc a new prop_bt here, remove all code involve alloc_if_needed
|
||||
* ...
|
||||
*
|
||||
* if (prop_offset != 0) {
|
||||
* atomic_store_explicit(¤t->prop, 0, memory_order_release); // Add this line to nullify the prop entry
|
||||
* return to_prop_info(¤t->prop);
|
||||
* } else {
|
||||
*
|
||||
* ....
|
||||
* }
|
||||
*
|
||||
*
|
||||
* by patching just those functions directly, all other functions should be ok
|
||||
* as is.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||
#include "_system_properties.h"
|
||||
#include "system_properties.h"
|
||||
#include "magisk.h"
|
||||
#include "resetprop.h"
|
||||
|
||||
#define PRINT_D(...) { LOGD(__VA_ARGS__); if (verbose) printf(__VA_ARGS__); }
|
||||
#define PRINT_E(...) { LOGE(__VA_ARGS__); fprintf(stderr, __VA_ARGS__); }
|
||||
#define PERSISTENT_PROPERTY_DIR "/data/property"
|
||||
|
||||
static int verbose = 0;
|
||||
|
||||
static bool is_legal_property_name(const char *name, size_t namelen) {
|
||||
|
||||
if (namelen < 1) return false;
|
||||
if (name[0] == '.') return false;
|
||||
if (name[namelen - 1] == '.') return false;
|
||||
|
||||
/* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
|
||||
/* Don't allow ".." to appear in a property name */
|
||||
for (size_t i = 0; i < namelen; i++) {
|
||||
if (name[i] == '.') {
|
||||
// i=0 is guaranteed to never have a dot. See above.
|
||||
if (name[i-1] == '.') return false;
|
||||
continue;
|
||||
}
|
||||
if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
|
||||
if (name[i] >= 'a' && name[i] <= 'z') continue;
|
||||
if (name[i] >= 'A' && name[i] <= 'Z') continue;
|
||||
if (name[i] >= '0' && name[i] <= '9') continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int usage(char* arg0) {
|
||||
fprintf(stderr,
|
||||
"resetprop v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu & nkk71) - System Props Modification Tool\n\n"
|
||||
"Usage: %s [options] [args...]\n"
|
||||
"%s <name> <value>: Set property entry <name> with <value>\n"
|
||||
"%s --file <prop file>: Load props from <prop file>\n"
|
||||
"%s --delete <name>: Remove prop entry <name>\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -v verbose output\n"
|
||||
" -n don't trigger events when changing props\n"
|
||||
" if used with deleteprop determines whether remove persist prop file\n"
|
||||
, arg0, arg0, arg0, arg0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int init_resetprop() {
|
||||
if (__system_properties_init2()) {
|
||||
PRINT_E("resetprop: Initialize error\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prop_exist(const char *name) {
|
||||
if (init_resetprop()) return 0;
|
||||
return __system_property_find2(name) != NULL;
|
||||
}
|
||||
|
||||
static void read_prop_info(void* cookie, const char *name, const char *value, uint32_t serial) {
|
||||
strcpy((char *) cookie, value);
|
||||
}
|
||||
|
||||
// Get prop by name, return string (should free manually!)
|
||||
char *getprop(const char *name) {
|
||||
if (init_resetprop()) return NULL;
|
||||
const prop_info *pi = __system_property_find2(name);
|
||||
if (pi == NULL) {
|
||||
PRINT_D("resetprop: prop [%s] does not exist\n", name);
|
||||
return NULL;
|
||||
}
|
||||
char value[PROP_VALUE_MAX];
|
||||
__system_property_read_callback2(pi, read_prop_info, value);
|
||||
PRINT_D("resetprop: getprop [%s]: [%s]\n", name, value);
|
||||
return strdup(value);
|
||||
}
|
||||
|
||||
static void (*cb)(const char *);
|
||||
|
||||
static void run_actual_cb(void* cookie, const char *name, const char *value, uint32_t serial) {
|
||||
cb(name);
|
||||
}
|
||||
|
||||
static void prop_foreach_cb(const prop_info* pi, void* cookie) {
|
||||
__system_property_read_callback2(pi, run_actual_cb, NULL);
|
||||
}
|
||||
|
||||
void getprop_all(void (*cbk)(const char *name)) {
|
||||
if (init_resetprop()) return;
|
||||
cb = cbk;
|
||||
__system_property_foreach2(prop_foreach_cb, NULL);
|
||||
}
|
||||
|
||||
int setprop(const char *name, const char *value) {
|
||||
return setprop2(name, value, 1);
|
||||
}
|
||||
|
||||
int setprop2(const char *name, const char *value, const int trigger) {
|
||||
if (init_resetprop()) return -1;
|
||||
int ret;
|
||||
|
||||
prop_info *pi = (prop_info*) __system_property_find2(name);
|
||||
if (pi != NULL) {
|
||||
if (trigger) {
|
||||
if (!strncmp(name, "ro.", 3)) deleteprop(name, trigger);
|
||||
ret = __system_property_set2(name, value);
|
||||
} else {
|
||||
ret = __system_property_update2(pi, value, strlen(value));
|
||||
}
|
||||
} else {
|
||||
PRINT_D("resetprop: New prop [%s]\n", name);
|
||||
if (trigger) {
|
||||
ret = __system_property_set2(name, value);
|
||||
} else {
|
||||
ret = __system_property_add2(name, strlen(name), value, strlen(value));
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_D("resetprop: setprop [%s]: [%s] by %s\n", name, value,
|
||||
trigger ? "property_service" : "modifing prop data structure");
|
||||
|
||||
if (ret)
|
||||
PRINT_E("resetprop: setprop error\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int deleteprop(const char *name, const int trigger) {
|
||||
if (init_resetprop()) return -1;
|
||||
PRINT_D("resetprop: deleteprop [%s]\n", name);
|
||||
if (__system_property_del(name)) {
|
||||
PRINT_D("resetprop: delete prop: [%s] error\n", name);
|
||||
return -1;
|
||||
}
|
||||
if (trigger && strstr(name, "persist.")) {
|
||||
char buffer[PATH_MAX];
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
|
||||
unlink(buffer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_prop_file(const char* filename, const int trigger) {
|
||||
if (init_resetprop()) return -1;
|
||||
PRINT_D("resetprop: Load prop file [%s]\n", filename);
|
||||
FILE *fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
PRINT_E("Cannot open [%s]\n", filename);
|
||||
return 1;
|
||||
}
|
||||
char *line = NULL, *pch;
|
||||
size_t len;
|
||||
ssize_t read;
|
||||
int comment = 0, i;
|
||||
while ((read = getline(&line, &len, fp)) != -1) {
|
||||
// Remove the trailing newline
|
||||
if (line[read - 1] == '\n') {
|
||||
line[read - 1] = '\0';
|
||||
--read;
|
||||
}
|
||||
comment = 0;
|
||||
for (i = 0; i < read; ++i) {
|
||||
// Ignore starting spaces
|
||||
if (line[i] == ' ') continue;
|
||||
else {
|
||||
// A line starting with # is ignored
|
||||
if (line[i] == '#') comment = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (comment) continue;
|
||||
pch = strchr(line, '=');
|
||||
// Ignore ivalid formats
|
||||
if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue;
|
||||
// Separate the string
|
||||
*pch = '\0';
|
||||
setprop2(line + i, pch + 1, trigger);
|
||||
}
|
||||
free(line);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int resetprop_main(int argc, char *argv[]) {
|
||||
|
||||
int del = 0, file = 0, trigger = 1;
|
||||
|
||||
int exp_arg = 2;
|
||||
char *name, *value, *filename;
|
||||
|
||||
if (argc < 3) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!strcmp("-v", argv[i])) {
|
||||
verbose = 1;
|
||||
} else if (!strcmp("-n", argv[i])) {
|
||||
trigger = 0;
|
||||
} else if (!strcmp("--file", argv[i])) {
|
||||
file = 1;
|
||||
exp_arg = 1;
|
||||
} else if (!strcmp("--delete", argv[i])) {
|
||||
del = 1;
|
||||
exp_arg = 1;
|
||||
} else {
|
||||
if (i + exp_arg > argc) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
if (file) {
|
||||
filename = argv[i];
|
||||
break;
|
||||
} else {
|
||||
if(!is_legal_property_name(argv[i], strlen(argv[i]))) {
|
||||
PRINT_E("Illegal property name: [%s]\n", argv[i]);
|
||||
return 1;
|
||||
}
|
||||
name = argv[i];
|
||||
if (exp_arg > 1) {
|
||||
if (strlen(argv[i + 1]) >= PROP_VALUE_MAX) {
|
||||
PRINT_E("Value too long: [%s]\n", argv[i + 1]);
|
||||
return 1;
|
||||
}
|
||||
value = argv[i + 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file) {
|
||||
return read_prop_file(filename, trigger);
|
||||
} else if (del) {
|
||||
return deleteprop(name, trigger);
|
||||
} else {
|
||||
return setprop2(name, value, trigger);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
23
jni/resetprop/resetprop.h
Normal file
23
jni/resetprop/resetprop.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* resetprop.h - API for resetprop
|
||||
*/
|
||||
|
||||
#ifndef _RESETPROP_H_
|
||||
#define _RESETPROP_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int prop_exist(const char *name);
|
||||
int setprop(const char *name, const char *value);
|
||||
int setprop2(const char *name, const char *value, const int trigger);
|
||||
char *getprop(const char *name);
|
||||
int deleteprop(const char *name, const int trigger);
|
||||
int read_prop_file(const char* filename, const int trigger);
|
||||
void getprop_all(void (*cbk)(const char *name));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1554
jni/resetprop/system_properties.cpp
Normal file
1554
jni/resetprop/system_properties.cpp
Normal file
File diff suppressed because it is too large
Load Diff
102
jni/resetprop/system_properties.h
Normal file
102
jni/resetprop/system_properties.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SYS_SYSTEM_PROPERTIES_H
|
||||
#define _INCLUDE_SYS_SYSTEM_PROPERTIES_H
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
typedef struct prop_info prop_info;
|
||||
|
||||
#define PROP_VALUE_MAX 92
|
||||
|
||||
/*
|
||||
* Sets system property `key` to `value`, creating the system property if it doesn't already exist.
|
||||
*/
|
||||
int __system_property_set2(const char* key, const char* value) __INTRODUCED_IN(12);
|
||||
|
||||
/*
|
||||
* Returns a `prop_info` corresponding system property `name`, or nullptr if it doesn't exist.
|
||||
* Use __system_property_read_callback to query the current value.
|
||||
*
|
||||
* Property lookup is expensive, so it can be useful to cache the result of this function.
|
||||
*/
|
||||
const prop_info* __system_property_find2(const char* name);
|
||||
|
||||
/*
|
||||
* Calls `callback` with a consistent trio of name, value, and serial number for property `pi`.
|
||||
*/
|
||||
void __system_property_read_callback2(const prop_info *pi,
|
||||
void (*callback)(void* cookie, const char *name, const char *value, uint32_t serial),
|
||||
void* cookie) __INTRODUCED_IN(26);
|
||||
|
||||
/*
|
||||
* Passes a `prop_info` for each system property to the provided
|
||||
* callback. Use __system_property_read_callback() to read the value.
|
||||
*
|
||||
* This method is for inspecting and debugging the property system, and not generally useful.
|
||||
*/
|
||||
int __system_property_foreach2(void (*propfn)(const prop_info* pi, void* cookie), void* cookie)
|
||||
__INTRODUCED_IN(19);
|
||||
|
||||
/*
|
||||
* Waits for the specific system property identified by `pi` to be updated
|
||||
* past `old_serial`. Waits no longer than `relative_timeout`, or forever
|
||||
* if `relaive_timeout` is null.
|
||||
*
|
||||
* If `pi` is null, waits for the global serial number instead.
|
||||
*
|
||||
* If you don't know the current serial, use 0.
|
||||
*
|
||||
* Returns true and updates `*new_serial_ptr` on success, or false if the call
|
||||
* timed out.
|
||||
*/
|
||||
struct timespec;
|
||||
bool __system_property_wait2(const prop_info* pi,
|
||||
uint32_t old_serial,
|
||||
uint32_t* new_serial_ptr,
|
||||
const struct timespec* relative_timeout)
|
||||
__INTRODUCED_IN(26);
|
||||
|
||||
/* Deprecated. In Android O and above, there's no limit on property name length. */
|
||||
#define PROP_NAME_MAX 32
|
||||
/* Deprecated. Use __system_property_read_callback instead. */
|
||||
int __system_property_read2(const prop_info* pi, char* name, char* value);
|
||||
/* Deprecated. Use __system_property_read_callback instead. */
|
||||
int __system_property_get2(const char* name, char* value);
|
||||
/* Deprecated. Use __system_property_foreach instead. */
|
||||
const prop_info* __system_property_find_nth2(unsigned n);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
Submodule jni/selinux updated: 91e9179a23...3d9b8bcb05
Submodule jni/sepolicy-inject deleted from 443af5f760
2
jni/su
2
jni/su
Submodule jni/su updated: d364ed2d77...86a113a536
148
jni/utils/img.c
Normal file
148
jni/utils/img.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/* img.c - All image related functions
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
#include <linux/loop.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int e2fsck(const char *img) {
|
||||
// Check and repair ext4 image
|
||||
char buffer[128];
|
||||
int pid, fd = -1;
|
||||
char *const command[] = { "e2fsck", "-yf", (char *) img, NULL };
|
||||
pid = run_command(1, &fd, "/system/bin/e2fsck", command);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
while (fdgets(buffer, sizeof(buffer), fd))
|
||||
LOGD("magisk_img: %s", buffer);
|
||||
waitpid(pid, NULL, 0);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *loopsetup(const char *img) {
|
||||
char device[20];
|
||||
struct loop_info64 info;
|
||||
int i, lfd, ffd;
|
||||
memset(&info, 0, sizeof(info));
|
||||
// First get an empty loop device
|
||||
for (i = 0; i <= 7; ++i) {
|
||||
sprintf(device, "/dev/block/loop%d", i);
|
||||
lfd = xopen(device, O_RDWR);
|
||||
if (ioctl(lfd, LOOP_GET_STATUS64, &info) == -1)
|
||||
break;
|
||||
close(lfd);
|
||||
}
|
||||
if (i == 8) return NULL;
|
||||
ffd = xopen(img, O_RDWR);
|
||||
if (ioctl(lfd, LOOP_SET_FD, ffd) == -1)
|
||||
return NULL;
|
||||
strcpy((char *) info.lo_file_name, img);
|
||||
ioctl(lfd, LOOP_SET_STATUS64, &info);
|
||||
close(lfd);
|
||||
close(ffd);
|
||||
return strdup(device);
|
||||
}
|
||||
|
||||
int create_img(const char *img, int size) {
|
||||
unlink(img);
|
||||
LOGI("Create %s with size %dM\n", img, size);
|
||||
// Create a temp file with the file contexts
|
||||
char file_contexts[] = "/magisk(/.*)? u:object_r:system_file:s0\n";
|
||||
// If not root, attempt to create in current diretory
|
||||
char *filename = getuid() == UID_ROOT ? "/dev/file_contexts_image" : "file_contexts_image";
|
||||
int pid, status, fd = xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
xwrite(fd, file_contexts, sizeof(file_contexts));
|
||||
close(fd);
|
||||
|
||||
char buffer[16];
|
||||
snprintf(buffer, sizeof(buffer), "%dM", size);
|
||||
char *const command[] = { "make_ext4fs", "-l", buffer, "-a", "/magisk", "-S", filename, (char *) img, NULL };
|
||||
pid = run_command(0, NULL, "/system/bin/make_ext4fs", command);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
waitpid(pid, &status, 0);
|
||||
unlink(filename);
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
int get_img_size(const char *img, int *used, int *total) {
|
||||
if (access(img, R_OK) == -1)
|
||||
return 1;
|
||||
char buffer[PATH_MAX];
|
||||
int pid, fd = -1, status = 1;
|
||||
char *const command[] = { "e2fsck", "-n", (char *) img, NULL };
|
||||
pid = run_command(1, &fd, "/system/bin/e2fsck", command);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
while (fdgets(buffer, sizeof(buffer), fd)) {
|
||||
if (strstr(buffer, img)) {
|
||||
char *tok = strtok(buffer, ",");
|
||||
while(tok != NULL) {
|
||||
if (strstr(tok, "blocks")) {
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
tok = strtok(NULL, ",");
|
||||
}
|
||||
if (status) continue;
|
||||
sscanf(tok, "%d/%d", used, total);
|
||||
*used = *used / 256 + 1;
|
||||
*total /= 256;
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
waitpid(pid, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int resize_img(const char *img, int size) {
|
||||
LOGI("Resize %s to %dM\n", img, size);
|
||||
if (e2fsck(img))
|
||||
return 1;
|
||||
char buffer[128];
|
||||
int pid, status, fd = -1;
|
||||
snprintf(buffer, sizeof(buffer), "%dM", size);
|
||||
char *const command[] = { "resize2fs", (char *) img, buffer, NULL };
|
||||
pid = run_command(1, &fd, "/system/bin/resize2fs", command);
|
||||
if (pid < 0)
|
||||
return 1;
|
||||
while (fdgets(buffer, sizeof(buffer), fd))
|
||||
LOGD("magisk_img: %s", buffer);
|
||||
close(fd);
|
||||
waitpid(pid, &status, 0);
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
char *mount_image(const char *img, const char *target) {
|
||||
if (access(img, F_OK) == -1)
|
||||
return NULL;
|
||||
if (access(target, F_OK) == -1) {
|
||||
if (xmkdir(target, 0755) == -1) {
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xmkdir(target, 0755);
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (e2fsck(img))
|
||||
return NULL;
|
||||
|
||||
char *device = loopsetup(img);
|
||||
if (device)
|
||||
xmount(device, target, "ext4", 0, NULL);
|
||||
return device;
|
||||
}
|
||||
|
||||
void umount_image(const char *target, const char *device) {
|
||||
xumount(target);
|
||||
int fd = xopen(device, O_RDWR);
|
||||
ioctl(fd, LOOP_CLR_FD);
|
||||
close(fd);
|
||||
}
|
||||
42
jni/utils/list.c
Normal file
42
jni/utils/list.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/* list.h - Double link list implementation
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
void init_list_head(struct list_head *head) {
|
||||
head->next = head;
|
||||
head->prev = head;
|
||||
}
|
||||
|
||||
void list_insert(struct list_head *pos, struct list_head *node) {
|
||||
// First construct our new node
|
||||
node->next = pos->next;
|
||||
node->prev = pos;
|
||||
// Maintain the list
|
||||
pos->next->prev = node;
|
||||
pos->next = node;
|
||||
}
|
||||
|
||||
void list_insert_end(struct list_head *head, struct list_head *node) {
|
||||
list_insert(head->prev, node);
|
||||
}
|
||||
|
||||
struct list_head *list_pop(struct list_head *pos) {
|
||||
struct list_head *ret;
|
||||
ret = pos->prev;
|
||||
// Maintain the list
|
||||
pos->prev->next = pos->next;
|
||||
pos->next->prev = pos->prev;
|
||||
// Remove references
|
||||
pos->next = pos;
|
||||
pos->prev = pos;
|
||||
// Return the previous node in the list
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct list_head *list_pop_end(struct list_head *head) {
|
||||
return list_pop(head->prev);
|
||||
}
|
||||
43
jni/utils/list.h
Normal file
43
jni/utils/list.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* list.h - Double link list implementation
|
||||
*/
|
||||
|
||||
#ifndef _LIST_H_
|
||||
#define _LIST_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next;
|
||||
struct list_head *prev;
|
||||
};
|
||||
|
||||
void init_list_head(struct list_head *head);
|
||||
void list_insert(struct list_head *pos, struct list_head *node);
|
||||
void list_insert_end(struct list_head *head, struct list_head *node);
|
||||
struct list_head *list_pop(struct list_head *pos);
|
||||
struct list_head *list_pop_end(struct list_head *head);
|
||||
|
||||
#define list_entry(pos, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (pos); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
|
||||
#define list_for_each(ptr, head, type, member) \
|
||||
ptr = list_entry((head)->next, type, member); \
|
||||
for (struct list_head *__ = (head)->next; __ != (head); __ = __->next, ptr = list_entry(__, type, member))
|
||||
|
||||
#define list_for_each_r(ptr, head, type, member) \
|
||||
ptr = list_entry((head)->prev, type, member); \
|
||||
for (struct list_head *__ = (head)->prev; __ != (head); __ = __->prev, ptr = list_entry(__, type, member))
|
||||
|
||||
#define list_destory(head, type, member, func) ({ \
|
||||
struct list_head *node = head->next; \
|
||||
while(node != head) { \
|
||||
node = node->next; \
|
||||
if (func) func(list_entry(node->prev, line_list, pos)); \
|
||||
free(list_entry(node->prev, line_list, pos)); \
|
||||
} \
|
||||
head->next = head; \
|
||||
head->prev = head; \
|
||||
})
|
||||
|
||||
#endif
|
||||
408
jni/utils/misc.c
Normal file
408
jni/utils/misc.c
Normal file
@@ -0,0 +1,408 @@
|
||||
/* misc.c - Store all functions that are unable to be catagorized clearly
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
|
||||
int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
|
||||
|
||||
unsigned get_shell_uid() {
|
||||
struct passwd* ppwd = getpwnam("shell");
|
||||
if (NULL == ppwd)
|
||||
return 2000;
|
||||
|
||||
return ppwd->pw_uid;
|
||||
}
|
||||
|
||||
unsigned get_system_uid() {
|
||||
struct passwd* ppwd = getpwnam("system");
|
||||
if (NULL == ppwd)
|
||||
return 1000;
|
||||
|
||||
return ppwd->pw_uid;
|
||||
}
|
||||
|
||||
unsigned get_radio_uid() {
|
||||
struct passwd* ppwd = getpwnam("radio");
|
||||
if (NULL == ppwd)
|
||||
return 1001;
|
||||
|
||||
return ppwd->pw_uid;
|
||||
}
|
||||
|
||||
int check_data() {
|
||||
int ret = 0;
|
||||
char buffer[4096];
|
||||
FILE *fp = xfopen("/proc/mounts", "r");
|
||||
while (fgets(buffer, sizeof(buffer), fp)) {
|
||||
if (strstr(buffer, " /data ")) {
|
||||
if (strstr(buffer, "tmpfs") == NULL)
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* All the string should be freed manually!! */
|
||||
int file_to_vector(const char* filename, struct vector *v) {
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t read;
|
||||
|
||||
FILE *fp = fopen(filename, "r");
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
|
||||
while ((read = getline(&line, &len, fp)) != -1) {
|
||||
// Remove end newline
|
||||
if (line[read - 1] == '\n')
|
||||
line[read - 1] = '\0';
|
||||
vec_push_back(v, line);
|
||||
line = NULL;
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vector_to_file(const char *filename, struct vector *v) {
|
||||
FILE *fp = xfopen(filename, "w");
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
char *line;
|
||||
vec_for_each(v, line) {
|
||||
fprintf(fp, "%s\n", line);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if the string only contains digits */
|
||||
int isNum(const char *s) {
|
||||
int len = strlen(s);
|
||||
for (int i = 0; i < len; ++i)
|
||||
if (s[i] < '0' || s[i] > '9')
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Read a whole line from file descriptor */
|
||||
ssize_t fdgets(char *buf, const size_t size, int fd) {
|
||||
ssize_t len = 0;
|
||||
buf[0] = '\0';
|
||||
while (read(fd, buf + len, 1) >= 0 && len < size - 1) {
|
||||
if (buf[len] == '\0' || buf[len++] == '\n') {
|
||||
buf[len] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf[size - 1] = '\0';
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Call func for each process */
|
||||
void ps(void (*func)(int)) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
if (!(dir = xopendir("/proc")))
|
||||
return;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (isNum(entry->d_name))
|
||||
func(atoi(entry->d_name));
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
// Internal usage
|
||||
static void (*ps_filter_cb)(int);
|
||||
static const char *ps_filter_pattern;
|
||||
static void proc_name_filter(int pid) {
|
||||
char buf[64];
|
||||
int fd;
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
|
||||
if ((fd = open(buf, O_RDONLY)) == -1)
|
||||
return;
|
||||
if (fdgets(buf, sizeof(buf), fd) == 0) {
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/comm", pid);
|
||||
close(fd);
|
||||
if ((fd = open(buf, O_RDONLY)) == -1)
|
||||
return;
|
||||
fdgets(buf, sizeof(buf), fd);
|
||||
}
|
||||
if (strcmp(buf, ps_filter_pattern) == 0) {
|
||||
ps_filter_cb(pid);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* Call func with process name filtered with pattern */
|
||||
void ps_filter_proc_name(const char *pattern, void (*func)(int)) {
|
||||
ps_filter_cb = func;
|
||||
ps_filter_pattern = ((pattern == NULL) ? "" : pattern);
|
||||
ps(proc_name_filter);
|
||||
}
|
||||
|
||||
int create_links(const char *bin, const char *path) {
|
||||
char self[PATH_MAX], linkpath[PATH_MAX];
|
||||
if (bin == NULL) {
|
||||
xreadlink("/proc/self/exe", self, sizeof(self));
|
||||
bin = self;
|
||||
}
|
||||
int ret = 0;
|
||||
for (int i = 0; applet[i]; ++i) {
|
||||
snprintf(linkpath, sizeof(linkpath), "%s/%s", path, applet[i]);
|
||||
unlink(linkpath);
|
||||
ret |= symlink(bin, linkpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define DEV_BLOCK "/dev/block"
|
||||
|
||||
void unlock_blocks() {
|
||||
char path[PATH_MAX];
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
int fd, OFF = 0;
|
||||
|
||||
if (!(dir = xopendir(DEV_BLOCK)))
|
||||
return;
|
||||
|
||||
while((entry = readdir(dir))) {
|
||||
if (entry->d_type == DT_BLK &&
|
||||
strstr(entry->d_name, "ram") == NULL &&
|
||||
strstr(entry->d_name, "loop") == NULL &&
|
||||
strstr(entry->d_name, "dm-0") == NULL) {
|
||||
snprintf(path, sizeof(path), "%s/%s", DEV_BLOCK, entry->d_name);
|
||||
if ((fd = xopen(path, O_RDONLY)) < 0)
|
||||
continue;
|
||||
|
||||
if (ioctl(fd, BLKROSET, &OFF) == -1)
|
||||
PLOGE("unlock %s", path);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void setup_sighandlers(void (*handler)(int)) {
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = handler;
|
||||
for (int i = 0; quit_signals[i]; ++i) {
|
||||
sigaction(quit_signals[i], &act, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fd == NULL -> Ignore output
|
||||
*fd < 0 -> Open pipe and set *fd to the read end
|
||||
*fd >= 0 -> STDOUT (or STDERR) will be redirected to *fd
|
||||
*/
|
||||
int run_command(int err, int *fd, const char *path, char *const argv[]) {
|
||||
int pipefd[2], writeEnd = -1;
|
||||
|
||||
if (fd) {
|
||||
if (*fd < 0) {
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return -1;
|
||||
writeEnd = pipefd[1];
|
||||
} else {
|
||||
writeEnd = *fd;
|
||||
}
|
||||
}
|
||||
|
||||
int pid = fork();
|
||||
if (pid != 0) {
|
||||
if (fd && *fd < 0) {
|
||||
// Give the read end and close write end
|
||||
*fd = pipefd[0];
|
||||
close(pipefd[1]);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
if (fd) {
|
||||
xdup2(writeEnd, STDOUT_FILENO);
|
||||
if (err) xdup2(writeEnd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
execv(path, argv);
|
||||
PLOGE("execv");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mkdir_p(const char *pathname, mode_t mode) {
|
||||
char *path = strdup(pathname), *p;
|
||||
errno = 0;
|
||||
for (p = path + 1; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\0';
|
||||
if (mkdir(path, mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
if (mkdir(path, mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bind_mount(const char *from, const char *to) {
|
||||
int ret = xmount(from, to, NULL, MS_BIND, NULL);
|
||||
#ifdef DEBUG
|
||||
LOGD("bind_mount: %s -> %s\n", from, to);
|
||||
#else
|
||||
LOGI("bind_mount: %s\n", to);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
int open_new(const char *filename) {
|
||||
return xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
}
|
||||
|
||||
// file/link -> file/link only!!
|
||||
int cp_afc(const char *source, const char *target) {
|
||||
struct stat buf;
|
||||
xlstat(source, &buf);
|
||||
unlink(target);
|
||||
char *con;
|
||||
if (S_ISREG(buf.st_mode)) {
|
||||
int sfd, tfd;
|
||||
sfd = xopen(source, O_RDONLY);
|
||||
tfd = xopen(target, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
xsendfile(tfd, sfd, NULL, buf.st_size);
|
||||
fclone_attr(sfd, tfd);
|
||||
close(sfd);
|
||||
close(tfd);
|
||||
} else if (S_ISLNK(buf.st_mode)) {
|
||||
char buffer[PATH_MAX];
|
||||
xreadlink(source, buffer, sizeof(buffer));
|
||||
xsymlink(buffer, target);
|
||||
lchown(target, buf.st_uid, buf.st_gid);
|
||||
lgetfilecon(source, &con);
|
||||
lsetfilecon(target, con);
|
||||
free(con);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clone_dir(const char *source, const char *target) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
char *s_path, *t_path;
|
||||
|
||||
if (!(dir = xopendir(source)))
|
||||
return 1;
|
||||
|
||||
s_path = xmalloc(PATH_MAX);
|
||||
t_path = xmalloc(PATH_MAX);
|
||||
|
||||
mkdir_p(target, 0755);
|
||||
clone_attr(source, target);
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
snprintf(s_path, PATH_MAX, "%s/%s", source, entry->d_name);
|
||||
snprintf(t_path, PATH_MAX, "%s/%s", target, entry->d_name);
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
clone_dir(s_path, t_path);
|
||||
break;
|
||||
case DT_REG:
|
||||
case DT_LNK:
|
||||
cp_afc(s_path, t_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(s_path);
|
||||
free(t_path);
|
||||
|
||||
closedir(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rm_rf(const char *target) {
|
||||
if (access(target, F_OK) == -1)
|
||||
return 0;
|
||||
// Use external rm command, saves a lot of headache and issues
|
||||
char command[PATH_MAX];
|
||||
snprintf(command, sizeof(command), "rm -rf %s", target);
|
||||
return system(command);
|
||||
}
|
||||
|
||||
void clone_attr(const char *source, const char *target) {
|
||||
struct stat buf;
|
||||
xstat(source, &buf);
|
||||
chmod(target, buf.st_mode & 0777);
|
||||
chown(target, buf.st_uid, buf.st_gid);
|
||||
char *con;
|
||||
lgetfilecon(source, &con);
|
||||
lsetfilecon(target, con);
|
||||
free(con);
|
||||
}
|
||||
|
||||
void fclone_attr(const int sourcefd, const int targetfd) {
|
||||
struct stat buf;
|
||||
fstat(sourcefd, &buf);
|
||||
fchmod(targetfd, buf.st_mode & 0777);
|
||||
fchown(targetfd, buf.st_uid, buf.st_gid);
|
||||
char *con;
|
||||
fgetfilecon(sourcefd, &con);
|
||||
fsetfilecon(sourcefd, con);
|
||||
free(con);
|
||||
}
|
||||
|
||||
void get_client_cred(int fd, struct ucred *cred) {
|
||||
socklen_t ucred_length = sizeof(*cred);
|
||||
if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length))
|
||||
PLOGE("getsockopt");
|
||||
}
|
||||
|
||||
int switch_mnt_ns(int pid) {
|
||||
char mnt[32];
|
||||
snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid);
|
||||
if(access(mnt, R_OK) == -1) return 1; // Maybe process died..
|
||||
|
||||
int fd, ret;
|
||||
fd = xopen(mnt, O_RDONLY);
|
||||
if (fd < 0) return 1;
|
||||
// Switch to its namespace
|
||||
ret = setns(fd, 0);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
104
jni/utils/utils.h
Normal file
104
jni/utils/utils.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/* util.h - Header for all utility functions
|
||||
*/
|
||||
|
||||
#ifndef _UTILS_H_
|
||||
#define _UTILS_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
#define UID_SHELL (get_shell_uid())
|
||||
#define UID_ROOT 0
|
||||
#define UID_SYSTEM (get_system_uid())
|
||||
#define UID_RADIO (get_radio_uid())
|
||||
|
||||
extern int quit_signals[];
|
||||
|
||||
// xwrap.c
|
||||
|
||||
FILE *xfopen(const char *pathname, const char *mode);
|
||||
FILE *xfdopen(int fd, const char *mode);
|
||||
#define GET_MACRO(_1, _2, _3, NAME, ...) NAME
|
||||
#define xopen(...) GET_MACRO(__VA_ARGS__, xopen3, xopen2)(__VA_ARGS__)
|
||||
int xopen2(const char *pathname, int flags);
|
||||
int xopen3(const char *pathname, int flags, mode_t mode);
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count);
|
||||
ssize_t xread(int fd, void *buf, size_t count);
|
||||
ssize_t xxread(int fd, void *buf, size_t count);
|
||||
int xpipe2(int pipefd[2], int flags);
|
||||
int xsetns(int fd, int nstype);
|
||||
DIR *xopendir(const char *name);
|
||||
struct dirent *xreaddir(DIR *dirp);
|
||||
pid_t xsetsid();
|
||||
int xsocket(int domain, int type, int protocol);
|
||||
int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int xconnect(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);
|
||||
void *xmalloc(size_t size);
|
||||
void *xcalloc(size_t nmemb, size_t size);
|
||||
void *xrealloc(void *ptr, size_t size);
|
||||
ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags);
|
||||
ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags);
|
||||
int xpthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg);
|
||||
int xsocketpair(int domain, int type, int protocol, int sv[2]);
|
||||
int xstat(const char *pathname, struct stat *buf);
|
||||
int xlstat(const char *pathname, struct stat *buf);
|
||||
int xdup2(int oldfd, int newfd);
|
||||
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz);
|
||||
int xsymlink(const char *target, const char *linkpath);
|
||||
int xmount(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data);
|
||||
int xumount(const char *target);
|
||||
int xumount2(const char *target, int flags);
|
||||
int xchmod(const char *pathname, mode_t mode);
|
||||
int xrename(const char *oldpath, const char *newpath);
|
||||
int xmkdir(const char *pathname, mode_t mode);
|
||||
void *xmmap(void *addr, size_t length, int prot, int flags,
|
||||
int fd, off_t offset);
|
||||
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
|
||||
int xmkdir_p(const char *pathname, mode_t mode);
|
||||
|
||||
// misc.c
|
||||
|
||||
unsigned get_shell_uid();
|
||||
unsigned get_system_uid();
|
||||
unsigned get_radio_uid();
|
||||
int check_data();
|
||||
int file_to_vector(const char* filename, struct vector *v);
|
||||
int vector_to_file(const char* filename, struct vector *v);
|
||||
int isNum(const char *s);
|
||||
ssize_t fdgets(char *buf, size_t size, int fd);
|
||||
void ps(void (*func)(int));
|
||||
void ps_filter_proc_name(const char *filter, void (*func)(int));
|
||||
int create_links(const char *bin, const char *path);
|
||||
void unlock_blocks();
|
||||
void setup_sighandlers(void (*handler)(int));
|
||||
int run_command(int err, int *fd, const char *path, char *const argv[]);
|
||||
int mkdir_p(const char *pathname, mode_t mode);
|
||||
int bind_mount(const char *from, const char *to);
|
||||
int open_new(const char *filename);
|
||||
int cp_afc(const char *source, const char *target);
|
||||
int clone_dir(const char *source, const char *target);
|
||||
int rm_rf(const char *target);
|
||||
void fclone_attr(const int sourcefd, const int targetfd);
|
||||
void clone_attr(const char *source, const char *target);
|
||||
void get_client_cred(int fd, struct ucred *cred);
|
||||
int switch_mnt_ns(int pid);
|
||||
|
||||
// img.c
|
||||
int create_img(const char *img, int size);
|
||||
int get_img_size(const char *img, int *used, int *total);
|
||||
int resize_img(const char *img, int size);
|
||||
char *mount_image(const char *img, const char *target);
|
||||
void umount_image(const char *target, const char *device);
|
||||
|
||||
#endif
|
||||
67
jni/utils/vector.c
Normal file
67
jni/utils/vector.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/* vector.c - A simple vector implementation in c
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
void vec_init(struct vector *v) {
|
||||
if (v == NULL) return;
|
||||
vec_size(v) = 0;
|
||||
vec_cap(v) = 1;
|
||||
vec_entry(v) = malloc(sizeof(void*));
|
||||
}
|
||||
|
||||
void vec_push_back(struct vector *v, void *p) {
|
||||
if (v == NULL) return;
|
||||
if (vec_size(v) == vec_cap(v)) {
|
||||
vec_cap(v) *= 2;
|
||||
vec_entry(v) = realloc(vec_entry(v), sizeof(void*) * vec_cap(v));
|
||||
}
|
||||
vec_entry(v)[vec_size(v)] = p;
|
||||
++vec_size(v);
|
||||
}
|
||||
|
||||
void *vec_pop_back(struct vector *v) {
|
||||
void *ret = vec_entry(v)[vec_size(v) - 1];
|
||||
--vec_size(v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void vec_sort(struct vector *v, int (*compar)(const void *, const void *)) {
|
||||
if (v == NULL) return;
|
||||
qsort(vec_entry(v), vec_size(v), sizeof(void*), compar);
|
||||
}
|
||||
|
||||
/* Will cleanup only the vector itself
|
||||
* use in cases when each element requires special cleanup
|
||||
*/
|
||||
void vec_destroy(struct vector *v) {
|
||||
if (v == NULL) return;
|
||||
vec_size(v) = 0;
|
||||
vec_cap(v) = 0;
|
||||
free(vec_entry(v));
|
||||
vec_entry(v) = NULL; // Prevent double destroy segfault
|
||||
}
|
||||
|
||||
/* Will cleanup each element AND the vector itself
|
||||
* Shall be the general case
|
||||
*/
|
||||
void vec_deep_destroy(struct vector *v) {
|
||||
if (v == NULL) return;
|
||||
void *e;
|
||||
vec_for_each(v, e) {
|
||||
free(e);
|
||||
}
|
||||
vec_destroy(v);
|
||||
}
|
||||
|
||||
struct vector *vec_dup(struct vector *v) {
|
||||
struct vector *ret = malloc(sizeof(*ret));
|
||||
vec_size(ret) = vec_size(v);
|
||||
vec_cap(ret) = vec_cap(v);
|
||||
vec_entry(v) = malloc(sizeof(void*) * vec_cap(ret));
|
||||
memcpy(vec_entry(ret), vec_entry(v), sizeof(void*) * vec_cap(ret));
|
||||
return ret;
|
||||
}
|
||||
35
jni/utils/vector.h
Normal file
35
jni/utils/vector.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* vector.h - A simple vector implementation in c
|
||||
*/
|
||||
|
||||
#ifndef _VECTOR_H_
|
||||
#define _VECTOR_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
struct vector {
|
||||
size_t size;
|
||||
size_t cap;
|
||||
void **data;
|
||||
};
|
||||
|
||||
void vec_init(struct vector *v);
|
||||
void vec_push_back(struct vector *v, void *p);
|
||||
void *vec_pop_back(struct vector *v);
|
||||
void vec_sort(struct vector *v, int (*compar)(const void *, const void *));
|
||||
void vec_destroy(struct vector *v);
|
||||
void vec_deep_destroy(struct vector *v);
|
||||
struct vector *vec_dup(struct vector *v);
|
||||
|
||||
#define vec_size(v) (v)->size
|
||||
#define vec_cap(v) (v)->cap
|
||||
#define vec_entry(v) (v)->data
|
||||
/* Usage: vec_for_each(vector *v, void *e) */
|
||||
#define vec_for_each(v, e) \
|
||||
e = v ? (v)->data[0] : NULL; \
|
||||
for (size_t _ = 0; v && _ < (v)->size; ++_, e = (v)->data[_])
|
||||
|
||||
#define vec_for_each_r(v, e) \
|
||||
e = v ? (v)->data[(v)->size - 1] : NULL; \
|
||||
for (size_t _ = (v)->size; v && _ > 0; --_, e = (v)->data[_ - 1])
|
||||
|
||||
#endif
|
||||
339
jni/utils/xwrap.c
Normal file
339
jni/utils/xwrap.c
Normal file
@@ -0,0 +1,339 @@
|
||||
/* xwrap.c - wrappers around existing library functions.
|
||||
*
|
||||
* Functions with the x prefix are wrappers that either succeed or kill the
|
||||
* program with an error message, but never return failure. They usually have
|
||||
* the same arguments and return value as the function they wrap.
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/sendfile.h>
|
||||
|
||||
#include "magisk.h"
|
||||
#include "utils.h"
|
||||
|
||||
FILE *xfopen(const char *pathname, const char *mode) {
|
||||
FILE *fp = fopen(pathname, mode);
|
||||
if (fp == NULL) {
|
||||
PLOGE("fopen: %s", pathname);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
FILE *xfdopen(int fd, const char *mode) {
|
||||
FILE *fp = fdopen(fd, mode);
|
||||
if (fp == NULL) {
|
||||
PLOGE("fopen");
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
int xopen2(const char *pathname, int flags) {
|
||||
int fd = open(pathname, flags);
|
||||
if (fd < 0) {
|
||||
PLOGE("open: %s", pathname);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int xopen3(const char *pathname, int flags, mode_t mode) {
|
||||
int fd = open(pathname, flags, mode);
|
||||
if (fd < 0) {
|
||||
PLOGE("open: %s", pathname);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count) {
|
||||
int ret = write(fd, buf, count);
|
||||
if (count != ret) {
|
||||
PLOGE("write");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Read error other than EOF
|
||||
ssize_t xread(int fd, void *buf, size_t count) {
|
||||
int ret = read(fd, buf, count);
|
||||
if (ret < 0) {
|
||||
PLOGE("read");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Read exact same size as count
|
||||
ssize_t xxread(int fd, void *buf, size_t count) {
|
||||
int ret = read(fd, buf, count);
|
||||
if (count != ret) {
|
||||
PLOGE("read");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xpipe2(int pipefd[2], int flags) {
|
||||
int ret = pipe2(pipefd, flags);
|
||||
if (ret == -1) {
|
||||
PLOGE("pipe2");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xsetns(int fd, int nstype) {
|
||||
int ret = setns(fd, nstype);
|
||||
if (ret == -1) {
|
||||
PLOGE("setns");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
DIR *xopendir(const char *name) {
|
||||
DIR *d = opendir(name);
|
||||
if (d == NULL) {
|
||||
PLOGE("opendir: %s", name);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
struct dirent *xreaddir(DIR *dirp) {
|
||||
errno = 0;
|
||||
struct dirent *e = readdir(dirp);
|
||||
if (errno && e == NULL) {
|
||||
PLOGE("readdir");
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
pid_t xsetsid() {
|
||||
pid_t pid = setsid();
|
||||
if (pid == -1) {
|
||||
PLOGE("setsid");
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
int xsocket(int domain, int type, int protocol) {
|
||||
int fd = socket(domain, type, protocol);
|
||||
if (fd == -1) {
|
||||
PLOGE("socket");
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
int ret = bind(sockfd, addr, addrlen);
|
||||
if (ret == -1) {
|
||||
PLOGE("bind");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
int ret = connect(sockfd, addr, addrlen);
|
||||
if (ret == -1) {
|
||||
PLOGE("bind");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xlisten(int sockfd, int backlog) {
|
||||
int ret = listen(sockfd, backlog);
|
||||
if (ret == -1) {
|
||||
PLOGE("listen");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
|
||||
int fd = accept4(sockfd, addr, addrlen, flags);
|
||||
if (fd == -1) {
|
||||
PLOGE("accept");
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
void *xmalloc(size_t size) {
|
||||
void *p = malloc(size);
|
||||
if (p == NULL) {
|
||||
PLOGE("malloc");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void *xcalloc(size_t nmemb, size_t size) {
|
||||
void *p = calloc(nmemb, size);
|
||||
if (p == NULL) {
|
||||
PLOGE("calloc");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void *xrealloc(void *ptr, size_t size) {
|
||||
void *p = realloc(ptr, size);
|
||||
if (p == NULL) {
|
||||
PLOGE("realloc");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags) {
|
||||
int sent = sendmsg(sockfd, msg, flags);
|
||||
if (sent == -1) {
|
||||
PLOGE("sendmsg");
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) {
|
||||
int rec = recvmsg(sockfd, msg, flags);
|
||||
if (rec == -1) {
|
||||
PLOGE("recvmsg");
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
int xpthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg) {
|
||||
errno = pthread_create(thread, attr, start_routine, arg);
|
||||
if (errno) {
|
||||
PLOGE("pthread_create");
|
||||
}
|
||||
return errno;
|
||||
}
|
||||
|
||||
int xsocketpair(int domain, int type, int protocol, int sv[2]) {
|
||||
int ret = socketpair(domain, type, protocol, sv);
|
||||
if (ret == -1) {
|
||||
PLOGE("socketpair");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xstat(const char *pathname, struct stat *buf) {
|
||||
int ret = stat(pathname, buf);
|
||||
if (ret == -1) {
|
||||
PLOGE("stat %s", pathname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xlstat(const char *pathname, struct stat *buf) {
|
||||
int ret = lstat(pathname, buf);
|
||||
if (ret == -1) {
|
||||
PLOGE("lstat %s", pathname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xdup2(int oldfd, int newfd) {
|
||||
int ret = dup2(oldfd, newfd);
|
||||
if (ret == -1) {
|
||||
PLOGE("dup2");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz) {
|
||||
ssize_t ret = readlink(pathname, buf, bufsiz);
|
||||
if (ret == -1) {
|
||||
PLOGE("readlink %s", pathname);
|
||||
} else {
|
||||
buf[ret] = '\0';
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xsymlink(const char *target, const char *linkpath) {
|
||||
int ret = symlink(target, linkpath);
|
||||
if (ret == -1) {
|
||||
PLOGE("symlink %s->%s", target, linkpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmount(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data) {
|
||||
int ret = mount(source, target, filesystemtype, mountflags, data);
|
||||
if (ret == -1) {
|
||||
PLOGE("mount %s->%s", source, target);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xumount(const char *target) {
|
||||
int ret = umount(target);
|
||||
if (ret == -1) {
|
||||
PLOGE("umount %s", target);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xumount2(const char *target, int flags) {
|
||||
int ret = umount2(target, flags);
|
||||
if (ret == -1) {
|
||||
PLOGE("umount2 %s", target);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xchmod(const char *pathname, mode_t mode) {
|
||||
int ret = chmod(pathname, mode);
|
||||
if (ret == -1) {
|
||||
PLOGE("chmod %s %u", pathname, mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xrename(const char *oldpath, const char *newpath) {
|
||||
int ret = rename(oldpath, newpath);
|
||||
if (ret == -1) {
|
||||
PLOGE("rename %s->%s", oldpath, newpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdir(const char *pathname, mode_t mode) {
|
||||
int ret = mkdir(pathname, mode);
|
||||
if (ret == -1 && errno != EEXIST) {
|
||||
PLOGE("mkdir %s %u", pathname, mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *xmmap(void *addr, size_t length, int prot, int flags,
|
||||
int fd, off_t offset) {
|
||||
void *ret = mmap(addr, length, prot, flags, fd, offset);
|
||||
if (ret == MAP_FAILED) {
|
||||
PLOGE("mmap");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count) {
|
||||
ssize_t ret = sendfile(out_fd, in_fd, offset, count);
|
||||
if (count != ret) {
|
||||
PLOGE("sendfile");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdir_p(const char *pathname, mode_t mode) {
|
||||
int ret = mkdir_p(pathname, mode);
|
||||
if (ret == -1) {
|
||||
PLOGE("mkdir_p %s", pathname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
97
scripts/addon.d.sh
Normal file
97
scripts/addon.d.sh
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/sbin/sh
|
||||
##########################################################################################
|
||||
#
|
||||
# Magisk Survival Script for ROMs with addon.d support
|
||||
# by topjohnwu
|
||||
#
|
||||
# Inspired by 99-flashafterupdate.sh of osm0sis @ xda-developers
|
||||
#
|
||||
##########################################################################################
|
||||
|
||||
. /tmp/backuptool.functions
|
||||
|
||||
main() {
|
||||
# Magisk binaries
|
||||
MAGISKBIN=/data/magisk
|
||||
# This script always run in recovery
|
||||
BOOTMODE=false
|
||||
|
||||
if [ ! -d $MAGISKBIN ]; then
|
||||
echo "! Cannot find Magisk binaries!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wait for post addon.d processes to finish
|
||||
sleep 5
|
||||
|
||||
mount -o ro /system 2>/dev/null
|
||||
mount -o ro /vendor 2>/dev/null
|
||||
mount /data 2>/dev/null
|
||||
|
||||
# Load utility functions
|
||||
. $MAGISKBIN/util_functions.sh
|
||||
|
||||
[ -f /system/build.prop ] || abort "! /system could not be mounted!"
|
||||
|
||||
ui_print "************************"
|
||||
ui_print "* MAGISK_VERSION_STUB"
|
||||
ui_print "************************"
|
||||
|
||||
api_level_arch_detect
|
||||
|
||||
# Check if system root is installed and remove
|
||||
remove_system_su
|
||||
|
||||
recovery_actions
|
||||
|
||||
find_boot_image
|
||||
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
|
||||
ui_print "- Found Boot Image: $BOOTIMAGE"
|
||||
|
||||
SOURCEDMODE=true
|
||||
cd $MAGISKBIN
|
||||
|
||||
# Source the boot patcher
|
||||
. $MAGISKBIN/boot_patch.sh "$BOOTIMAGE"
|
||||
|
||||
[ -f stock_boot* ] && rm -f /data/stock_boot* 2>/dev/null
|
||||
|
||||
ui_print "- Flashing new boot image"
|
||||
if [ -L "$BOOTIMAGE" ]; then
|
||||
dd if=new-boot.img of="$BOOTIMAGE" bs=4096
|
||||
else
|
||||
cat new-boot.img /dev/zero | dd of="$BOOTIMAGE" bs=4096 >/dev/null 2>&1
|
||||
fi
|
||||
rm -f new-boot.img
|
||||
|
||||
cd /
|
||||
|
||||
recovery_cleanup
|
||||
|
||||
ui_print "- Done"
|
||||
exit 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
backup)
|
||||
# Stub
|
||||
;;
|
||||
restore)
|
||||
# Stub
|
||||
;;
|
||||
pre-backup)
|
||||
# Stub
|
||||
;;
|
||||
post-backup)
|
||||
# Stub
|
||||
;;
|
||||
pre-restore)
|
||||
# Stub
|
||||
;;
|
||||
post-restore)
|
||||
# Get the FD for ui_print
|
||||
OUTFD=`ps | grep -v grep | grep -oE "update(.*)" | cut -d" " -f3`
|
||||
# Run the main function in a parallel subshell
|
||||
(main) &
|
||||
;;
|
||||
esac
|
||||
252
scripts/boot_patch.sh
Normal file
252
scripts/boot_patch.sh
Normal file
@@ -0,0 +1,252 @@
|
||||
#!/system/bin/sh
|
||||
##########################################################################################
|
||||
#
|
||||
# Magisk Boot Image Patcher
|
||||
# by topjohnwu
|
||||
#
|
||||
# This script should be placed in a directory with the following files:
|
||||
#
|
||||
# File name type Description
|
||||
#
|
||||
# boot_patch.sh script A script to patch boot. Expect path to boot image as parameter.
|
||||
# (this file) The script will use binaries and files in its same directory
|
||||
# to complete the patching process
|
||||
# magisk binary The main binary for all Magisk operations.
|
||||
# It is also used to patch the sepolicy in the ramdisk.
|
||||
# magiskboot binary A tool to unpack boot image, decompress ramdisk, extract ramdisk
|
||||
# , and patch the ramdisk for Magisk support
|
||||
# init.magisk.rc script A new line will be added to init.rc to import this script.
|
||||
# All magisk entrypoints are defined here
|
||||
# chromeos folder This folder should store all the utilities and keys to sign
|
||||
# (optional) a chromeos device, used in the tablet Pixel C
|
||||
#
|
||||
# If the script is not running as root, then the input boot image should be a stock image
|
||||
# or have a backup included in ramdisk internally, since we cannot access the stock boot
|
||||
# image placed under /data we've created when previously installing
|
||||
#
|
||||
##########################################################################################
|
||||
##########################################################################################
|
||||
# Functions
|
||||
##########################################################################################
|
||||
|
||||
# Call ui_print_wrap if exists, or else simply use echo
|
||||
# Useful when wrapped in flashable zip
|
||||
ui_print_wrap() {
|
||||
type ui_print >/dev/null 2>&1 && ui_print "$1" || echo "$1"
|
||||
}
|
||||
|
||||
# Call abort if exists, or else show error message and exit
|
||||
# Essential when wrapped in flashable zip
|
||||
abort_wrap() {
|
||||
type abort >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
ui_print_wrap "$1"
|
||||
exit 1
|
||||
else
|
||||
abort "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# Pure bash dirname implementation
|
||||
dirname_wrap() {
|
||||
if echo $1 | grep "/" >/dev/null 2>&1; then
|
||||
RES=${1%/*}
|
||||
[ -z $RES ] && echo "/" || echo $RES
|
||||
else
|
||||
echo "."
|
||||
fi
|
||||
}
|
||||
|
||||
# Pure bash basename implementation
|
||||
basename_wrap() {
|
||||
echo ${1##*/}
|
||||
}
|
||||
|
||||
grep_prop() {
|
||||
REGEX="s/^$1=//p"
|
||||
shift
|
||||
FILES=$@
|
||||
if [ -z "$FILES" ]; then
|
||||
FILES='/system/build.prop'
|
||||
fi
|
||||
cat $FILES 2>/dev/null | sed -n "$REGEX" | head -n 1
|
||||
}
|
||||
|
||||
# --cpio-add <incpio> <mode> <entry> <infile>
|
||||
cpio_add() {
|
||||
./magiskboot --cpio-add ramdisk.cpio $1 $2 $3
|
||||
}
|
||||
|
||||
# --cpio-extract <incpio> <entry> <outfile>
|
||||
cpio_extract() {
|
||||
./magiskboot --cpio-extract ramdisk.cpio $1 $2
|
||||
}
|
||||
|
||||
# --cpio-mkdir <incpio> <mode> <entry>
|
||||
cpio_mkdir() {
|
||||
./magiskboot --cpio-mkdir ramdisk.cpio $1 $2
|
||||
}
|
||||
|
||||
##########################################################################################
|
||||
# Initialization
|
||||
##########################################################################################
|
||||
|
||||
CWD=`pwd`
|
||||
cd "`dirname_wrap $1`"
|
||||
BOOTIMAGE="`pwd`/`basename_wrap $1`"
|
||||
cd "$CWD"
|
||||
|
||||
if [ -z "$BOOTIMAGE" ]; then
|
||||
ui_print_wrap "This script requires a boot image as a parameter"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Presets
|
||||
[ -z $KEEPVERITY ] && KEEPVERITY=false
|
||||
[ -z $KEEPFORCEENCRYPT ] && KEEPFORCEENCRYPT=false
|
||||
|
||||
# Detect whether running as root
|
||||
[ `id -u` -eq 0 ] && ROOT=true || ROOT=false
|
||||
|
||||
# Switch to the location of the script file
|
||||
[ -z $SOURCEDMODE ] && cd "`dirname_wrap "${BASH_SOURCE:-$0}"`"
|
||||
chmod +x ./*
|
||||
|
||||
##########################################################################################
|
||||
# Unpack
|
||||
##########################################################################################
|
||||
|
||||
ui_print_wrap "- Unpacking boot image"
|
||||
./magiskboot --unpack "$BOOTIMAGE"
|
||||
|
||||
CHROMEOS=false
|
||||
case $? in
|
||||
1 )
|
||||
abort_wrap "! Unable to unpack boot image"
|
||||
;;
|
||||
2 )
|
||||
CHROMEOS=true
|
||||
;;
|
||||
3 )
|
||||
ui_print_wrap "! Sony ELF32 format detected"
|
||||
abort_wrap "! Please use BootBridge from @AdrianDC to flash Magisk"
|
||||
;;
|
||||
4 )
|
||||
ui_print_wrap "! Sony ELF64 format detected"
|
||||
abort_wrap "! Stock kernel cannot be patched, please use a custom kernel"
|
||||
esac
|
||||
|
||||
##########################################################################################
|
||||
# Ramdisk restores
|
||||
##########################################################################################
|
||||
|
||||
# Test patch status and do restore, after this section, ramdisk.cpio.orig is guaranteed to exist
|
||||
ui_print_wrap "- Checking ramdisk status"
|
||||
./magiskboot --cpio-test ramdisk.cpio
|
||||
case $? in
|
||||
0 ) # Stock boot
|
||||
ui_print_wrap "- Stock boot image detected!"
|
||||
ui_print_wrap "- Backing up stock boot image"
|
||||
SHA1=`./magiskboot --sha1 "$BOOTIMAGE" | tail -n 1`
|
||||
STOCKDUMP=stock_boot_${SHA1}.img
|
||||
dd if="$BOOTIMAGE" of=$STOCKDUMP
|
||||
./magiskboot --compress $STOCKDUMP
|
||||
cp -af ramdisk.cpio ramdisk.cpio.orig
|
||||
;;
|
||||
1 ) # Magisk patched
|
||||
ui_print_wrap "- Magisk patched image detected!"
|
||||
# Find SHA1 of stock boot image
|
||||
if [ -z $SHA1 ]; then
|
||||
./magiskboot --cpio-extract ramdisk.cpio init.magisk.rc init.magisk.rc.old
|
||||
SHA1=`grep_prop "# STOCKSHA1" init.magisk.rc.old`
|
||||
rm -f init.magisk.rc.old
|
||||
fi
|
||||
|
||||
OK=false
|
||||
./magiskboot --cpio-restore ramdisk.cpio
|
||||
if [ $? -eq 0 ]; then
|
||||
ui_print_wrap "- Ramdisk restored from internal backup"
|
||||
OK=true
|
||||
else
|
||||
# Restore failed
|
||||
ui_print_wrap "! Cannot restore from internal backup"
|
||||
# If we are root and SHA1 known, we try to find the stock backup
|
||||
if $ROOT && [ ! -z $SHA1 ]; then
|
||||
STOCKDUMP=/data/stock_boot_${SHA1}.img
|
||||
if [ -f ${STOCKDUMP}.gz ]; then
|
||||
ui_print_wrap "- Stock boot image backup found"
|
||||
./magiskboot --decompress ${STOCKDUMP}.gz stock_boot.img
|
||||
./magiskboot --unpack stock_boot.img
|
||||
rm -f stock_boot.img
|
||||
OK=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if ! $OK; then
|
||||
ui_print_wrap "! Ramdisk restoration incomplete"
|
||||
ui_print_wrap "! Will still try to continue installation"
|
||||
fi
|
||||
cp -af ramdisk.cpio ramdisk.cpio.orig
|
||||
;;
|
||||
2 ) # Other patched
|
||||
ui_print_wrap "! Boot image patched by other programs!"
|
||||
abort_wrap "! Please restore stock boot image"
|
||||
;;
|
||||
esac
|
||||
|
||||
##########################################################################################
|
||||
# Ramdisk patches
|
||||
##########################################################################################
|
||||
|
||||
ui_print_wrap "- Patching ramdisk"
|
||||
|
||||
# Add magisk entrypoint
|
||||
./magiskboot --cpio-patch ramdisk.cpio $KEEPVERITY $KEEPFORCEENCRYPT
|
||||
|
||||
# sepolicy patches
|
||||
cpio_extract sepolicy sepolicy
|
||||
./magisk magiskpolicy --load sepolicy --save sepolicy --minimal
|
||||
cpio_add 644 sepolicy sepolicy
|
||||
rm -f sepolicy
|
||||
|
||||
# Add new items
|
||||
if [ ! -z $SHA1 ]; then
|
||||
cp init.magisk.rc init.magisk.rc.bak
|
||||
echo "# STOCKSHA1=$SHA1" >> init.magisk.rc
|
||||
fi
|
||||
cpio_add 750 init.magisk.rc init.magisk.rc
|
||||
mv init.magisk.rc.bak init.magisk.rc 2>/dev/null
|
||||
cpio_add 755 sbin/magisk magisk
|
||||
|
||||
# Create ramdisk backups
|
||||
./magiskboot --cpio-backup ramdisk.cpio ramdisk.cpio.orig
|
||||
|
||||
rm -f ramdisk.cpio.orig
|
||||
|
||||
##########################################################################################
|
||||
# Repack and flash
|
||||
##########################################################################################
|
||||
|
||||
# Hexpatches
|
||||
|
||||
# Remove Samsung RKP in stock kernel
|
||||
./magiskboot --hexpatch kernel \
|
||||
49010054011440B93FA00F71E9000054010840B93FA00F7189000054001840B91FA00F7188010054 \
|
||||
A1020054011440B93FA00F7140020054010840B93FA00F71E0010054001840B91FA00F7181010054
|
||||
|
||||
ui_print_wrap "- Repacking boot image"
|
||||
./magiskboot --repack "$BOOTIMAGE" || abort_wrap "! Unable to repack boot image!"
|
||||
|
||||
# Sign chromeos boot
|
||||
if $CHROMEOS; then
|
||||
echo > empty
|
||||
|
||||
./chromeos/futility vbutil_kernel --pack new-boot.img.signed \
|
||||
--keyblock ./chromeos/kernel.keyblock --signprivate ./chromeos/kernel_data_key.vbprivk \
|
||||
--version 1 --vmlinuz new-boot.img --config empty --arch arm --bootloader empty --flags 0x1
|
||||
|
||||
rm -f empty new-boot.img
|
||||
mv new-boot.img.signed new-boot.img
|
||||
fi
|
||||
|
||||
./magiskboot --cleanup
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
RAMDISK=$1
|
||||
BINDIR=/data/magisk
|
||||
[ ! -e $BINDIR ] && BINDIR=/cache/data_bin
|
||||
[ ! -e $BINDIR ] && exit
|
||||
SYSTEMLIB=/system/lib
|
||||
[ -d /system/lib64 ] && SYSTEMLIB=/system/lib64
|
||||
|
||||
cpio_add() {
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --cpio-add $RAMDISK $RAMDISK $2 $1 $1
|
||||
}
|
||||
|
||||
cpio_extract() {
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --cpio-extract $RAMDISK $1 $1
|
||||
}
|
||||
|
||||
cpio_mkdir() {
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --cpio-mkdir $RAMDISK $RAMDISK $2 $1
|
||||
}
|
||||
|
||||
# Recursive
|
||||
cpio_rm() {
|
||||
if [ "$1" = "-r" ]; then
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --cpio-ls $RAMDISK | grep "^$2/" | while read i ; do
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --cpio-rm $RAMDISK $RAMDISK $i
|
||||
done
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --cpio-rmdir $RAMDISK $RAMDISK $2
|
||||
else
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --cpio-rm $RAMDISK $RAMDISK $1
|
||||
fi
|
||||
}
|
||||
|
||||
file_contain() {
|
||||
grep "$1" "$2" >/dev/null 2>&1
|
||||
return $?
|
||||
}
|
||||
|
||||
rm -rf /tmp/magisk/ramdisk 2>/dev/null
|
||||
mkdir -p /tmp/magisk/ramdisk
|
||||
cd /tmp/magisk/ramdisk
|
||||
|
||||
cat $RAMDISK | cpio -i
|
||||
|
||||
# Cleanup SuperSU backups
|
||||
cpio_rm -r .subackup
|
||||
|
||||
# Add magisk entrypoint
|
||||
for RC in init*.rc; do
|
||||
if file_contain "import /init.environ.rc" $RC && ! file_contain "import /init.magisk.rc" $RC; then
|
||||
sed -i "/import \/init\.environ\.rc/iimport /init.magisk.rc" $RC
|
||||
cpio_add $RC 750
|
||||
fi
|
||||
if file_contain "selinux.reload_policy" $RC; then
|
||||
sed -i "/selinux.reload_policy/d" $RC
|
||||
cpio_add $RC 750
|
||||
fi
|
||||
done
|
||||
|
||||
# sepolicy patches
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB $BINDIR/sepolicy-inject --load sepolicy --save sepolicy --minimal
|
||||
cpio_add sepolicy 644
|
||||
|
||||
# Add new items
|
||||
cp -af $BINDIR/init.magisk.rc init.magisk.rc
|
||||
cp -af $BINDIR/magic_mask.sh sbin/magic_mask.sh
|
||||
|
||||
cpio_mkdir magisk 755
|
||||
cpio_add init.magisk.rc 750
|
||||
cpio_add sbin/magic_mask.sh 750
|
||||
@@ -1,357 +1,118 @@
|
||||
#!/sbin/sh
|
||||
##########################################################################################
|
||||
#
|
||||
# Magisk Boot Image Patcher
|
||||
# Magisk Flash Script
|
||||
# by topjohnwu
|
||||
#
|
||||
# This zip will patch your boot image with Magisk support
|
||||
# This script will detect, construct the environment for Magisk
|
||||
# It will then call boot_patch.sh to patch the boot image
|
||||
#
|
||||
##########################################################################################
|
||||
|
||||
if [ -z "$BOOTMODE" ]; then
|
||||
BOOTMODE=false
|
||||
fi
|
||||
##########################################################################################
|
||||
# Preparation
|
||||
##########################################################################################
|
||||
|
||||
TMPDIR=/tmp
|
||||
($BOOTMODE) && TMPDIR=/dev/tmp
|
||||
# Detect whether in boot mode
|
||||
ps | grep zygote | grep -v grep >/dev/null && BOOTMODE=true || BOOTMODE=false
|
||||
$BOOTMODE || ps -A 2>/dev/null | grep zygote | grep -v grep >/dev/null && BOOTMODE=true
|
||||
|
||||
INSTALLER=$TMPDIR/magisk
|
||||
# This path should work in any cases
|
||||
TMPDIR=/dev/tmp
|
||||
|
||||
COREDIR=/magisk/.core
|
||||
|
||||
# Boot Image Variables
|
||||
INSTALLER=$TMPDIR/install
|
||||
COMMONDIR=$INSTALLER/common
|
||||
CHROMEDIR=$INSTALLER/chromeos
|
||||
NEWBOOT=$TMPDIR/boottmp/new-boot.img
|
||||
UNPACKDIR=$TMPDIR/boottmp/bootunpack
|
||||
RAMDISK=$TMPDIR/boottmp/ramdisk
|
||||
COREDIR=/magisk/.core
|
||||
|
||||
# Default permissions
|
||||
umask 022
|
||||
|
||||
##########################################################################################
|
||||
# Flashable update-binary preparation
|
||||
##########################################################################################
|
||||
|
||||
OUTFD=$2
|
||||
ZIP=$3
|
||||
|
||||
readlink /proc/$$/fd/$OUTFD 2>/dev/null | grep /tmp >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
OUTFD=0
|
||||
rm -rf $TMPDIR 2>/dev/null
|
||||
mkdir -p $INSTALLER
|
||||
unzip -o "$ZIP" -d $INSTALLER 2>/dev/null
|
||||
|
||||
for FD in `ls /proc/$$/fd`; do
|
||||
readlink /proc/$$/fd/$FD 2>/dev/null | grep pipe >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
ps | grep " 3 $FD " | grep -v grep >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
OUTFD=$FD
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ ! -d "$COMMONDIR" ]; then
|
||||
echo "! Unable to extract zip file!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p $INSTALLER
|
||||
cd $INSTALLER
|
||||
unzip -o "$ZIP"
|
||||
# Load utility fuctions
|
||||
. $COMMONDIR/util_functions.sh
|
||||
|
||||
##########################################################################################
|
||||
# Functions
|
||||
##########################################################################################
|
||||
|
||||
ui_print() {
|
||||
if ($BOOTMODE); then
|
||||
echo "$1"
|
||||
else
|
||||
echo -n -e "ui_print $1\n" >> /proc/self/fd/$OUTFD
|
||||
echo -n -e "ui_print\n" >> /proc/self/fd/$OUTFD
|
||||
fi
|
||||
}
|
||||
|
||||
getvar() {
|
||||
local VARNAME=$1
|
||||
local VALUE=$(eval echo \$"$VARNAME");
|
||||
for FILE in /dev/.magisk /data/.magisk /cache/.magisk /system/.magisk; do
|
||||
if [ -z "$VALUE" ]; then
|
||||
LINE=$(cat $FILE 2>/dev/null | grep "$VARNAME=")
|
||||
if [ ! -z "$LINE" ]; then
|
||||
VALUE=${LINE#*=}
|
||||
fi
|
||||
fi
|
||||
done
|
||||
eval $VARNAME=\$VALUE
|
||||
}
|
||||
|
||||
find_boot_image() {
|
||||
if [ -z "$BOOTIMAGE" ]; then
|
||||
for PARTITION in kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do
|
||||
BOOTIMAGE=`readlink /dev/block/by-name/$PARTITION || readlink /dev/block/platform/*/by-name/$PARTITION || readlink /dev/block/platform/*/*/by-name/$PARTITION`
|
||||
if [ ! -z "$BOOTIMAGE" ]; then break; fi
|
||||
done
|
||||
fi
|
||||
if [ -z "$BOOTIMAGE" ]; then
|
||||
FSTAB="/etc/recovery.fstab"
|
||||
[ ! -f "$FSTAB" ] && FSTAB="/etc/recovery.fstab.bak"
|
||||
[ -f "$FSTAB" ] && BOOTIMAGE=`grep -E '\b/boot\b' "$FSTAB" | grep -oE '/dev/[a-zA-Z0-9_./-]*'`
|
||||
fi
|
||||
}
|
||||
|
||||
is_mounted() {
|
||||
if [ ! -z "$2" ]; then
|
||||
cat /proc/mounts | grep $1 | grep $2, >/dev/null
|
||||
else
|
||||
cat /proc/mounts | grep $1 >/dev/null
|
||||
fi
|
||||
return $?
|
||||
}
|
||||
|
||||
mount_image() {
|
||||
if [ ! -d "$2" ]; then
|
||||
mount -o rw,remount rootfs /
|
||||
mkdir -p $2 2>/dev/null
|
||||
($BOOTMODE) && mount -o ro,remount rootfs /
|
||||
[ ! -d "$2" ] && return 1
|
||||
fi
|
||||
if (! is_mounted $2); then
|
||||
LOOPDEVICE=
|
||||
for LOOP in 0 1 2 3 4 5 6 7; do
|
||||
if (! is_mounted $2); then
|
||||
LOOPDEVICE=/dev/block/loop$LOOP
|
||||
if [ ! -f "$LOOPDEVICE" ]; then
|
||||
mknod $LOOPDEVICE b 7 $LOOP 2>/dev/null
|
||||
fi
|
||||
losetup $LOOPDEVICE $1
|
||||
if [ "$?" -eq "0" ]; then
|
||||
mount -t ext4 -o loop $LOOPDEVICE $2
|
||||
if (! is_mounted $2); then
|
||||
/system/bin/toolbox mount -t ext4 -o loop $LOOPDEVICE $2
|
||||
fi
|
||||
if (! is_mounted $2); then
|
||||
/system/bin/toybox mount -t ext4 -o loop $LOOPDEVICE $2
|
||||
fi
|
||||
fi
|
||||
if (is_mounted $2); then
|
||||
ui_print "- Mounting $1 to $2"
|
||||
break;
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
grep_prop() {
|
||||
REGEX="s/^$1=//p"
|
||||
shift
|
||||
FILES=$@
|
||||
if [ -z "$FILES" ]; then
|
||||
FILES='/system/build.prop'
|
||||
fi
|
||||
cat $FILES 2>/dev/null | sed -n $REGEX | head -n 1
|
||||
}
|
||||
|
||||
file_contain() {
|
||||
grep "$1" "$2" >/dev/null 2>&1
|
||||
return $?
|
||||
}
|
||||
|
||||
unpack_boot() {
|
||||
rm -rf $UNPACKDIR $RAMDISK 2>/dev/null
|
||||
mkdir -p $UNPACKDIR
|
||||
mkdir -p $RAMDISK
|
||||
cd $UNPACKDIR
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB $BINDIR/bootimgtools --extract $1
|
||||
|
||||
[ ! -f $UNPACKDIR/ramdisk.gz ] && return 1
|
||||
|
||||
cd $RAMDISK
|
||||
gunzip -c < $UNPACKDIR/ramdisk.gz | cpio -i
|
||||
}
|
||||
|
||||
repack_boot() {
|
||||
if (! $SUPERSU); then
|
||||
cd $RAMDISK
|
||||
find . | cpio -o -H newc 2>/dev/null | gzip -9 > $UNPACKDIR/ramdisk.gz
|
||||
fi
|
||||
cd $UNPACKDIR
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB $BINDIR/bootimgtools --repack $BOOTIMAGE
|
||||
if [ -f chromeos ]; then
|
||||
cp -af $CHROMEDIR /data/magisk
|
||||
echo " " > config
|
||||
echo " " > bootloader
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB $CHROMEDIR/futility vbutil_kernel --pack new-boot.img.signed --keyblock $CHROMEDIR/kernel.keyblock --signprivate $CHROMEDIR/kernel_data_key.vbprivk --version 1 --vmlinuz new-boot.img --config config --arch arm --bootloader bootloader --flags 0x1
|
||||
rm -f new-boot.img
|
||||
mv new-boot.img.signed new-boot.img
|
||||
fi
|
||||
if ($SAMSUNG); then
|
||||
SAMSUNG_CHECK=$(cat new-boot.img | grep SEANDROIDENFORCE)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -n "SEANDROIDENFORCE" >> new-boot.img
|
||||
fi
|
||||
fi
|
||||
if ($LGE_G); then
|
||||
# Prevent secure boot error on LG G2/G3.
|
||||
# Just for know, It's a pattern which bootloader verifies at boot. Thanks to LG hackers.
|
||||
echo -n -e "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79" >> new-boot.img
|
||||
fi
|
||||
mv new-boot.img $NEWBOOT
|
||||
}
|
||||
|
||||
remove_system_su() {
|
||||
if [ -f /system/bin/su -o -f /system/xbin/su ] && [ ! -f /su/bin/su ]; then
|
||||
ui_print "! System installed root detected, mount rw :("
|
||||
mount -o rw,remount /system
|
||||
# SuperSU
|
||||
if [ -e /system/bin/.ext/.su ]; then
|
||||
mv -f /system/bin/app_process32_original /system/bin/app_process32 2>/dev/null
|
||||
mv -f /system/bin/app_process64_original /system/bin/app_process64 2>/dev/null
|
||||
mv -f /system/bin/install-recovery_original.sh /system/bin/install-recovery.sh 2>/dev/null
|
||||
cd /system/bin
|
||||
if [ -e app_process64 ]; then
|
||||
ln -sf app_process64 app_process
|
||||
else
|
||||
ln -sf app_process32 app_process
|
||||
fi
|
||||
fi
|
||||
rm -rf /system/.pin /system/bin/.ext /system/etc/.installed_su_daemon /system/etc/.has_su_daemon \
|
||||
/system/xbin/daemonsu /system/xbin/su /system/xbin/sugote /system/xbin/sugote-mksh /system/xbin/supolicy \
|
||||
/system/bin/app_process_init /system/bin/su /cache/su /system/lib/libsupol.so /system/lib64/libsupol.so \
|
||||
/system/su.d /system/etc/install-recovery.sh /system/etc/init.d/99SuperSUDaemon /cache/install-recovery.sh \
|
||||
/system/.supersu /cache/.supersu /data/.supersu \
|
||||
/system/app/Superuser.apk /system/app/SuperSU /cache/Superuser.apk 2>/dev/null
|
||||
fi
|
||||
}
|
||||
get_outfd
|
||||
|
||||
##########################################################################################
|
||||
# Detection
|
||||
##########################################################################################
|
||||
|
||||
ui_print "*****************************"
|
||||
ui_print "MAGISK_VERSION_STUB"
|
||||
ui_print "*****************************"
|
||||
ui_print "************************"
|
||||
ui_print "* MAGISK_VERSION_STUB"
|
||||
ui_print "************************"
|
||||
|
||||
if [ ! -d "$INSTALLER/common" ]; then
|
||||
ui_print "! Failed: Unable to extract zip file!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ui_print "- Mounting /system(ro), /cache, /data"
|
||||
ui_print "- Mounting /system, /vendor, /cache, /data"
|
||||
mount -o ro /system 2>/dev/null
|
||||
mount -o ro /vendor 2>/dev/null
|
||||
mount /cache 2>/dev/null
|
||||
mount /data 2>/dev/null
|
||||
|
||||
if [ ! -f '/system/build.prop' ]; then
|
||||
ui_print "! Failed: /system could not be mounted!"
|
||||
exit 1
|
||||
fi
|
||||
[ -f /system/build.prop ] || abort "! /system could not be mounted!"
|
||||
|
||||
if [ -z "$NOOVERRIDE" ]; then
|
||||
# read override variables
|
||||
getvar KEEPVERITY
|
||||
getvar KEEPFORCEENCRYPT
|
||||
getvar BOOTIMAGE
|
||||
fi
|
||||
# read override variables
|
||||
getvar KEEPVERITY
|
||||
getvar KEEPFORCEENCRYPT
|
||||
getvar BOOTIMAGE
|
||||
|
||||
if [ -z "$KEEPVERITY" ]; then
|
||||
# we don't keep dm-verity by default
|
||||
KEEPVERITY=false
|
||||
fi
|
||||
if [ -z "$KEEPFORCEENCRYPT" ]; then
|
||||
# we don't keep forceencrypt by default
|
||||
KEEPFORCEENCRYPT=false
|
||||
fi
|
||||
# Detect version and architecture
|
||||
api_level_arch_detect
|
||||
|
||||
[ $API -lt 21 ] && abort "! Magisk is only for Lollipop 5.0+ (SDK 21+)"
|
||||
|
||||
# Check if system root is installed and remove
|
||||
remove_system_su
|
||||
|
||||
SAMSUNG=false
|
||||
SAMSUNG_CHECK=$(cat /system/build.prop | grep "ro.build.fingerprint=" | grep -i "samsung")
|
||||
if [ $? -eq 0 ]; then
|
||||
SAMSUNG=true
|
||||
fi
|
||||
|
||||
LGE_G=false
|
||||
RBRAND=$(grep_prop ro.product.brand)
|
||||
RMODEL=$(grep_prop ro.product.device)
|
||||
if [ "$RBRAND" = "lge" ] || [ "$RBRAND" = "LGE" ]; then
|
||||
if [ "$RMODEL" = "*D80*" ] ||
|
||||
[ "$RMODEL" = "*S98*" ] ||
|
||||
[ "$RMODEL" = "*D85*" ] ||
|
||||
[ "$RMODEL" = "*F40*" ]; then
|
||||
LGE_G=true
|
||||
ui_print "! Bump device detected"
|
||||
fi
|
||||
fi
|
||||
|
||||
API=$(grep_prop ro.build.version.sdk)
|
||||
ABI=$(grep_prop ro.product.cpu.abi | cut -c-3)
|
||||
ABI2=$(grep_prop ro.product.cpu.abi2 | cut -c-3)
|
||||
ABILONG=$(grep_prop ro.product.cpu.abi)
|
||||
|
||||
ARCH=arm
|
||||
IS64BIT=false
|
||||
if [ "$ABI" = "x86" ]; then ARCH=x86; fi;
|
||||
if [ "$ABI2" = "x86" ]; then ARCH=x86; fi;
|
||||
if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=true; fi;
|
||||
if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=true; fi;
|
||||
|
||||
|
||||
if [ "$API" -lt "21" ]; then
|
||||
ui_print "! Magisk is only for Lollipop 5.0+ (SDK 21+)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ui_print "- Device platform: $ARCH"
|
||||
|
||||
BINDIR=$INSTALLER/$ARCH
|
||||
chmod -R 755 $CHROMEDIR/futility $BINDIR
|
||||
|
||||
SYSTEMLIB=/system/lib
|
||||
($IS64BIT) && SYSTEMLIB=/system/lib64
|
||||
chmod -R 755 $CHROMEDIR $BINDIR
|
||||
|
||||
find_boot_image
|
||||
if [ -z "$BOOTIMAGE" ]; then
|
||||
ui_print "! Unable to detect boot image"
|
||||
exit 1
|
||||
fi
|
||||
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
|
||||
|
||||
##########################################################################################
|
||||
# Environment
|
||||
##########################################################################################
|
||||
|
||||
ui_print "- Constructing environment"
|
||||
|
||||
is_mounted /data && MAGISKBIN=/data/magisk || MAGISKBIN=/cache/data_bin
|
||||
|
||||
if (is_mounted /data); then
|
||||
rm -rf /data/magisk 2>/dev/null
|
||||
mkdir -p /data/magisk
|
||||
cp -af $BINDIR/busybox $BINDIR/sepolicy-inject $BINDIR/resetprop $BINDIR/bootimgtools \
|
||||
$INSTALLER/common/init.magisk.rc $INSTALLER/common/magic_mask.sh /data/magisk
|
||||
cp -af $INSTALLER/common/magisk.apk /data/magisk.apk
|
||||
chmod -R 755 /data/magisk
|
||||
chcon -h u:object_r:system_file:s0 /data/magisk /data/magisk/*
|
||||
PATH=/data/busybox:$PATH
|
||||
else
|
||||
rm -rf /cache/data_bin 2>/dev/null
|
||||
mkdir -p /cache/data_bin
|
||||
cp -af $BINDIR/busybox $BINDIR/sepolicy-inject $BINDIR/resetprop $BINDIR/bootimgtools \
|
||||
$INSTALLER/common/custom_ramdisk_patch.sh $INSTALLER/common/init.magisk.rc \
|
||||
$INSTALLER/common/magic_mask.sh /cache/data_bin
|
||||
cp -af $INSTALLER/common/magisk.apk /cache/magisk.apk
|
||||
chmod -R 755 /cache/data_bin
|
||||
# Copy required files
|
||||
rm -rf $MAGISKBIN 2>/dev/null
|
||||
mkdir -p $MAGISKBIN
|
||||
cp -af $BINDIR/. $COMMONDIR/. $MAGISKBIN
|
||||
cp -af $CHROMEDIR $MAGISKBIN
|
||||
chmod -R 755 $MAGISKBIN
|
||||
|
||||
# addon.d
|
||||
if [ -d /system/addon.d ]; then
|
||||
ui_print "- Adding addon.d survival script"
|
||||
mount -o rw,remount /system
|
||||
cp $INSTALLER/addon.d/99-magisk.sh /system/addon.d/99-magisk.sh
|
||||
chmod 755 /system/addon.d/99-magisk.sh
|
||||
fi
|
||||
|
||||
# Temporary busybox for installation
|
||||
mkdir -p $TMPDIR/busybox
|
||||
$BINDIR/busybox --install -s $TMPDIR/busybox
|
||||
rm -f $TMPDIR/busybox/su $TMPDIR/busybox/sh $TMPDIR/busybox/reboot
|
||||
PATH=$TMPDIR/busybox:$PATH
|
||||
|
||||
|
||||
##########################################################################################
|
||||
# Image
|
||||
# Magisk Image
|
||||
##########################################################################################
|
||||
|
||||
$BOOTMODE || recovery_actions
|
||||
|
||||
# Fix SuperSU.....
|
||||
($BOOTMODE) && $BINDIR/sepolicy-inject --live "allow fsck * * *"
|
||||
$BOOTMODE && $MAGISKBIN/magisk magiskpolicy --live "allow fsck * * *"
|
||||
|
||||
if (is_mounted /data); then
|
||||
IMG=/data/magisk.img
|
||||
@@ -364,217 +125,64 @@ if [ -f $IMG ]; then
|
||||
ui_print "- $IMG detected!"
|
||||
else
|
||||
ui_print "- Creating $IMG"
|
||||
make_ext4fs -l 64M -a /magisk -S $INSTALLER/common/file_contexts_image $IMG
|
||||
$MAGISKBIN/magisk --createimg $IMG 64M
|
||||
fi
|
||||
|
||||
mount_image $IMG /magisk
|
||||
if (! is_mounted /magisk); then
|
||||
ui_print "! Magisk image mount failed..."
|
||||
exit 1
|
||||
if ! is_mounted /magisk; then
|
||||
ui_print "- Mounting $IMG to /magisk"
|
||||
MAGISKLOOP=`$MAGISKBIN/magisk --mountimg $IMG /magisk`
|
||||
fi
|
||||
MAGISKLOOP=$LOOPDEVICE
|
||||
is_mounted /magisk || abort "! Magisk image mount failed..."
|
||||
|
||||
# Core folders and scripts
|
||||
mkdir -p $COREDIR/magiskhide $COREDIR/post-fs-data.d $COREDIR/service.d 2>/dev/null
|
||||
cp -af $INSTALLER/common/magiskhide/. $BINDIR/magiskhide $COREDIR/magiskhide
|
||||
chmod -R 755 $COREDIR/magiskhide $COREDIR/post-fs-data.d $COREDIR/service.d
|
||||
chown -R 0.0 $COREDIR/magiskhide $COREDIR/post-fs-data.d $COREDIR/service.d
|
||||
# Core folders
|
||||
mkdir -p $COREDIR/props $COREDIR/post-fs-data.d $COREDIR/service.d 2>/dev/null
|
||||
|
||||
chmod 755 $COREDIR/post-fs-data.d $COREDIR/service.d
|
||||
chown 0.0 $COREDIR/post-fs-data.d $COREDIR/service.d
|
||||
|
||||
# Legacy cleanup
|
||||
mv $COREDIR/magiskhide/hidelist $COREDIR/hidelist 2>/dev/null
|
||||
rm -rf $COREDIR/magiskhide $COREDIR/bin
|
||||
|
||||
##########################################################################################
|
||||
# Boot image patch
|
||||
# Unpack boot
|
||||
##########################################################################################
|
||||
|
||||
ui_print "- Found Boot Image: $BOOTIMAGE"
|
||||
|
||||
rm -rf $TMPDIR/boottmp 2>/dev/null
|
||||
mkdir -p $TMPDIR/boottmp
|
||||
|
||||
ui_print "- Unpacking boot image"
|
||||
unpack_boot $BOOTIMAGE
|
||||
if [ $? -ne 0 ]; then
|
||||
ui_print "! Unable to unpack boot image"
|
||||
exit 1;
|
||||
# Update our previous backup to new format if exists
|
||||
if [ -f /data/stock_boot.img ]; then
|
||||
SHA1=`$MAGISKBIN/magiskboot --sha1 /data/stock_boot.img | tail -n 1`
|
||||
STOCKDUMP=/data/stock_boot_${SHA1}.img
|
||||
mv /data/stock_boot.img $STOCKDUMP
|
||||
$MAGISKBIN/magiskboot --compress $STOCKDUMP
|
||||
fi
|
||||
|
||||
ORIGBOOT=
|
||||
SUPERSU=false
|
||||
[ -f sbin/launch_daemonsu.sh ] && SUPERSU=true
|
||||
SOURCEDMODE=true
|
||||
cd $MAGISKBIN
|
||||
|
||||
if ($SUPERSU); then
|
||||
# Source the boot patcher
|
||||
. $COMMONDIR/boot_patch.sh "$BOOTIMAGE"
|
||||
|
||||
##############################
|
||||
# SuperSU installation process
|
||||
##############################
|
||||
|
||||
ui_print "- SuperSU patched boot detected!"
|
||||
ui_print "- Adding auto patch script for SuperSU"
|
||||
cp -af $INSTALLER/common/custom_ramdisk_patch.sh /data/custom_ramdisk_patch.sh
|
||||
if (is_mounted /data); then
|
||||
SUIMG=/data/su.img
|
||||
else
|
||||
SUIMG=/cache/su.img
|
||||
fi
|
||||
mount_image $SUIMG /su
|
||||
if (! is_mounted /su); then
|
||||
ui_print "! SU image mount failed..."
|
||||
ui_print "! Please immediately flash SuperSU now"
|
||||
ui_print "! Installation will complete after flashing SuperSU"
|
||||
exit 1
|
||||
fi
|
||||
SUPERSULOOP=$LOOPDEVICE
|
||||
gunzip -c < $UNPACKDIR/ramdisk.gz > $UNPACKDIR/ramdisk
|
||||
ui_print "- Using sukernel to restore ramdisk"
|
||||
# Restore ramdisk
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --cpio-restore $UNPACKDIR/ramdisk $UNPACKDIR/ramdisk.orig
|
||||
if [ $? -ne 0 ]; then
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --restore $UNPACKDIR/ramdisk $TMPDIR/boottmp/stock_boot.img
|
||||
if [ $? -ne 0 ]; then
|
||||
ui_print "! Unable to restore ramdisk"
|
||||
exit 1
|
||||
fi
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --bootimg-extract-ramdisk $TMPDIR/boottmp/stock_boot.img $UNPACKDIR/ramdisk.orig.gz
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --ungzip $UNPACKDIR/ramdisk.orig.gz $UNPACKDIR/ramdisk.orig
|
||||
fi
|
||||
if [ ! -f $UNPACKDIR/ramdisk.orig ]; then
|
||||
ui_print "! Unable to restore ramdisk"
|
||||
exit 1
|
||||
fi
|
||||
rm -f $TMPDIR/boottmp/stock_boot.img $UNPACKDIR/ramdisk.orig.gz $UNPACKDIR/ramdisk.gz 2>/dev/null
|
||||
ui_print "- Patching ramdisk with sukernel"
|
||||
sh /data/custom_ramdisk_patch.sh $UNPACKDIR/ramdisk
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB /su/bin/sukernel --cpio-backup $UNPACKDIR/ramdisk.orig $UNPACKDIR/ramdisk $UNPACKDIR/ramdisk
|
||||
gzip -9 < $UNPACKDIR/ramdisk > $UNPACKDIR/ramdisk.gz
|
||||
rm -f $UNPACKDIR/ramdisk $UNPACKDIR/ramdisk.orig
|
||||
|
||||
else
|
||||
|
||||
##############################
|
||||
# Magisk installation process
|
||||
##############################
|
||||
|
||||
# Ramdisk restore
|
||||
if [ -d ".backup" ]; then
|
||||
# This implies Magisk is already installed, and ramdisk backup exists
|
||||
ui_print "- Restoring ramdisk with ramdisk backup"
|
||||
cp -af .backup/. .
|
||||
rm -rf magisk init.magisk.rc sbin/magic_mask.sh 2>/dev/null
|
||||
ORIGBOOT=false
|
||||
elif [ -d "magisk" ]; then
|
||||
mv -f /data/stock_boot_*.gz /data/stock_boot.img.gz 2>/dev/null
|
||||
gzip -d /data/stock_boot.img.gz 2>/dev/null
|
||||
rm -f /data/stock_boot.img.gz 2>/dev/null
|
||||
[ -f /data/stock_boot.img ] && ORIGBOOT=/data/stock_boot.img
|
||||
# If Magisk is installed and no SuperSU and no ramdisk backups,
|
||||
# we restore previous stock boot image backups
|
||||
if [ ! -z $ORIGBOOT ]; then
|
||||
ui_print "- Restoring boot image with backup"
|
||||
unpack_boot $ORIGBOOT
|
||||
fi
|
||||
# Removing possible modifications
|
||||
rm -rf magisk init.magisk.rc sbin/magic_mask.sh sbin/su init.xposed.rc sbin/mount_xposed.sh 2>/dev/null
|
||||
ORIGBOOT=false
|
||||
fi
|
||||
|
||||
# Backups
|
||||
ui_print "- Creating ramdisk backup"
|
||||
mkdir .backup 2>/dev/null
|
||||
cp -af *fstab* verity_key sepolicy .backup 2>/dev/null
|
||||
if [ -z $ORIGBOOT ]; then
|
||||
ui_print "- Creating boot image backup"
|
||||
if (is_mounted /data); then
|
||||
dd if=$BOOTIMAGE of=/data/stock_boot.img
|
||||
else
|
||||
dd if=$BOOTIMAGE of=/cache/stock_boot.img
|
||||
fi
|
||||
fi
|
||||
|
||||
# MagiskSU
|
||||
ui_print "- Installing MagiskSU"
|
||||
mkdir -p $COREDIR/su 2>/dev/null
|
||||
cp -af $BINDIR/su $INSTALLER/common/magisksu.sh $COREDIR/su
|
||||
chmod 755 $COREDIR/su/su $COREDIR/su/magisksu.sh
|
||||
chown -R 0.0 $COREDIR/su/su $COREDIR/su/magisksu.sh
|
||||
|
||||
# Patch ramdisk
|
||||
ui_print "- Patching ramdisk"
|
||||
|
||||
# Add magisk entrypoints
|
||||
for RC in init*.rc; do
|
||||
if file_contain "import /init.environ.rc" $RC && ! file_contain "import /init.magisk.rc" $RC; then
|
||||
[ ! -f .backup/$RC ] && cp -af $RC .backup
|
||||
sed -i "/import \/init\.environ\.rc/iimport /init.magisk.rc" $RC
|
||||
fi
|
||||
if file_contain "selinux.reload_policy" $RC; then
|
||||
[ ! -f .backup/$RC ] && cp -af $RC .backup
|
||||
sed -i "/selinux.reload_policy/d" $RC
|
||||
fi
|
||||
done
|
||||
|
||||
for FSTAB in *fstab*; do
|
||||
[ -L $FSTAB ] && continue
|
||||
if (! $KEEPVERITY); then
|
||||
sed -i "s/,support_scfs//g" $FSTAB
|
||||
sed -i 's/,\{0,1\}verify\(=[^,]*\)\{0,1\}//g' $FSTAB
|
||||
fi
|
||||
if (! $KEEPFORCEENCRYPT); then
|
||||
sed -i "s/forceencrypt/encryptable/g" $FSTAB
|
||||
sed -i "s/forcefdeorfbe/encryptable/g" $FSTAB
|
||||
fi
|
||||
done
|
||||
if (! $KEEPVERITY); then
|
||||
rm verity_key 2>/dev/null
|
||||
fi
|
||||
|
||||
# minimal sepolicy patches
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB $BINDIR/sepolicy-inject --load sepolicy --save sepolicy --minimal
|
||||
|
||||
# Add new items
|
||||
mkdir -p magisk 2>/dev/null
|
||||
cp -af $INSTALLER/common/init.magisk.rc init.magisk.rc
|
||||
cp -af $INSTALLER/common/magic_mask.sh sbin/magic_mask.sh
|
||||
|
||||
chmod 0755 magisk
|
||||
chmod 0750 init.magisk.rc sbin/magic_mask.sh
|
||||
chown 0.0 magisk init.magisk.rc sbin/magic_mask.sh
|
||||
if [ -f stock_boot* ]; then
|
||||
rm -f /data/stock_boot* 2>/dev/null
|
||||
mv stock_boot* /data
|
||||
fi
|
||||
|
||||
ui_print "- Repacking boot image"
|
||||
repack_boot
|
||||
|
||||
BOOTSIZE=`blockdev --getsize64 $BOOTIMAGE 2>/dev/null`
|
||||
NEWSIZE=`ls -l $NEWBOOT | awk '{print $5}'`
|
||||
if [ "$NEWSIZE" -gt "$BOOTSIZE" ]; then
|
||||
ui_print "! Boot partition space insufficient"
|
||||
ui_print "! Remove ramdisk backups and try again"
|
||||
rm -rf $RAMDISK/.backup $NEWBOOT 2>/dev/null
|
||||
repack_boot
|
||||
NEWSIZE=`ls -l $NEWBOOT | awk '{print $5}'`
|
||||
if [ "$NEWSIZE" -gt "$BOOTSIZE" ]; then
|
||||
ui_print "! Boot partition size still too small..."
|
||||
ui_print "! Unable to install Magisk"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
chmod 644 $NEWBOOT
|
||||
|
||||
ui_print "- Flashing new boot image"
|
||||
[ ! -L $BOOTIMAGE ] && dd if=/dev/zero of=$BOOTIMAGE bs=4096 2>/dev/null
|
||||
dd if=$NEWBOOT of=$BOOTIMAGE bs=4096
|
||||
if [ -L "$BOOTIMAGE" ]; then
|
||||
dd if=new-boot.img of="$BOOTIMAGE" bs=4096
|
||||
else
|
||||
cat new-boot.img /dev/zero | dd of="$BOOTIMAGE" bs=4096 >/dev/null 2>&1
|
||||
fi
|
||||
rm -f new-boot.img
|
||||
|
||||
cd /
|
||||
|
||||
if (! $BOOTMODE); then
|
||||
ui_print "- Unmounting partitions"
|
||||
umount /magisk
|
||||
losetup -d $MAGISKLOOP
|
||||
if ! $BOOTMODE; then
|
||||
$MAGISKBIN/magisk --umountimg /magisk $MAGISKLOOP
|
||||
rmdir /magisk
|
||||
if ($SUPERSU); then
|
||||
umount /su
|
||||
losetup -d $SUPERSULOOP
|
||||
rmdir /su
|
||||
fi
|
||||
umount /system
|
||||
recovery_cleanup
|
||||
fi
|
||||
|
||||
ui_print "- Done"
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
on post-fs
|
||||
start magisk_pfs
|
||||
wait /dev/.magisk.unblock 20
|
||||
rm /dev/.magisk.unblock
|
||||
|
||||
on post-fs-data
|
||||
rm /dev/.magisk.unblock
|
||||
load_persist_props
|
||||
start magisk_pfsd
|
||||
wait /dev/.magisk.unblock 60
|
||||
rm /dev/.magisk.unblock
|
||||
|
||||
on property:magisk.restart_pfsd=1
|
||||
trigger post-fs-data
|
||||
@@ -17,19 +16,19 @@ on property:magisk.restart_pfsd=1
|
||||
# Services
|
||||
|
||||
# launch post-fs script
|
||||
service magisk_pfs /sbin/magic_mask.sh post-fs
|
||||
service magisk_pfs /sbin/magisk --post-fs
|
||||
user root
|
||||
seclabel u:r:su:s0
|
||||
oneshot
|
||||
|
||||
# launch post-fs-data script
|
||||
service magisk_pfsd /sbin/magic_mask.sh post-fs-data
|
||||
service magisk_pfsd /sbin/magisk --post-fs-data
|
||||
user root
|
||||
seclabel u:r:su:s0
|
||||
oneshot
|
||||
|
||||
# launch late_start script
|
||||
service magisk_service /sbin/magic_mask.sh service
|
||||
service magisk_service /sbin/magisk --service
|
||||
class late_start
|
||||
user root
|
||||
seclabel u:r:su:s0
|
||||
|
||||
@@ -1,527 +0,0 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
LOGFILE=/cache/magisk.log
|
||||
DISABLEFILE=/cache/.disable_magisk
|
||||
UNINSTALLER=/cache/magisk_uninstaller.sh
|
||||
IMG=/data/magisk.img
|
||||
WHITELIST="/system/bin"
|
||||
|
||||
MOUNTPOINT=/magisk
|
||||
|
||||
COREDIR=$MOUNTPOINT/.core
|
||||
|
||||
TMPDIR=/dev/magisk
|
||||
DUMMDIR=$TMPDIR/dummy
|
||||
MIRRDIR=$TMPDIR/mirror
|
||||
MOUNTINFO=$TMPDIR/mnt
|
||||
|
||||
# Use the included busybox for maximum compatibility and reliable results
|
||||
# e.g. we rely on the option "-c" for cp (reserve contexts), and -exec for find
|
||||
TOOLPATH=/dev/busybox
|
||||
BINPATH=/data/magisk
|
||||
OLDPATH=$PATH
|
||||
export PATH=$TOOLPATH:$OLDPATH
|
||||
|
||||
APPDIR=/data/data/com.topjohnwu.magisk/files
|
||||
|
||||
# Default permissions
|
||||
umask 022
|
||||
|
||||
log_print() {
|
||||
echo "$1"
|
||||
echo "$1" >> $LOGFILE
|
||||
log -p i -t Magisk "$1"
|
||||
}
|
||||
|
||||
mktouch() {
|
||||
mkdir -p "${1%/*}" 2>/dev/null
|
||||
if [ -z "$2" ]; then
|
||||
touch "$1" 2>/dev/null
|
||||
else
|
||||
echo "$2" > "$1" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
in_list() {
|
||||
for i in $2; do
|
||||
[ "$1" = "$i" ] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
unblock() {
|
||||
touch /dev/.magisk.unblock
|
||||
chcon u:object_r:device:s0 /dev/.magisk.unblock
|
||||
exit
|
||||
}
|
||||
|
||||
run_scripts() {
|
||||
BASE=$MOUNTPOINT
|
||||
for MOD in $BASE/* ; do
|
||||
if [ ! -f $MOD/disable ]; then
|
||||
if [ -f $MOD/$1.sh ]; then
|
||||
chmod 755 $MOD/$1.sh
|
||||
chcon u:object_r:system_file:s0 $MOD/$1.sh
|
||||
log_print "$1: $MOD/$1.sh"
|
||||
sh $MOD/$1.sh
|
||||
fi
|
||||
fi
|
||||
done
|
||||
for SCRIPT in $COREDIR/${1}.d/* ; do
|
||||
if [ -f "$SCRIPT" ]; then
|
||||
chmod 755 $SCRIPT
|
||||
chcon u:object_r:system_file:s0 $SCRIPT
|
||||
log_print "${1}.d: $SCRIPT"
|
||||
sh $SCRIPT
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
loopsetup() {
|
||||
LOOPDEVICE=
|
||||
for DEV in `ls /dev/block/loop*`; do
|
||||
if losetup $DEV $1; then
|
||||
LOOPDEVICE=$DEV
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
target_size_check() {
|
||||
e2fsck -p -f "$1"
|
||||
curBlocks=`e2fsck -n $1 2>/dev/null | cut -d, -f3 | cut -d\ -f2`;
|
||||
curUsedM=$((`echo "$curBlocks" | cut -d/ -f1` * 4 / 1024));
|
||||
curSizeM=$((`echo "$curBlocks" | cut -d/ -f2` * 4 / 1024));
|
||||
curFreeM=$((curSizeM - curUsedM));
|
||||
}
|
||||
|
||||
travel() {
|
||||
# Ignore /system/vendor, we will handle it differently
|
||||
[ "$1" = "system/vendor" ] && return
|
||||
|
||||
cd "$TRAVEL_ROOT/$1"
|
||||
if [ -f .replace ]; then
|
||||
rm -rf "$MOUNTINFO/$1"
|
||||
mktouch "$MOUNTINFO/$1" "$TRAVEL_ROOT"
|
||||
else
|
||||
for ITEM in * ; do
|
||||
# This means it an empty folder (shouldn't happen, but better to be safe)
|
||||
[ "$ITEM" = "*" ] && return;
|
||||
# Target not found or target/file is a symlink
|
||||
if [ ! -e "/$1/$ITEM" -o -L "/$1/$ITEM" -o -L "$ITEM" ]; then
|
||||
# If we are in a higher level, delete the lower levels
|
||||
rm -rf "$MOUNTINFO/dummy/$1" 2>/dev/null
|
||||
# Mount the dummy parent
|
||||
mktouch "$MOUNTINFO/dummy/$1"
|
||||
|
||||
if [ -d "$ITEM" ]; then
|
||||
# Create new dummy directory and mount it
|
||||
mkdir -p "$DUMMDIR/$1/$ITEM"
|
||||
mktouch "$MOUNTINFO/$1/$ITEM" "$TRAVEL_ROOT"
|
||||
elif [ -L "$ITEM" ]; then
|
||||
# Copy symlinks
|
||||
mkdir -p "$DUMMDIR/$1" 2>/dev/null
|
||||
cp -afc "$ITEM" $"DUMMDIR/$1/$ITEM"
|
||||
else
|
||||
# Create new dummy file and mount it
|
||||
mktouch "$DUMMDIR/$1/$ITEM"
|
||||
mktouch "$MOUNTINFO/$1/$ITEM" "$TRAVEL_ROOT"
|
||||
fi
|
||||
else
|
||||
if [ -d "$ITEM" ]; then
|
||||
# It's an directory, travel deeper
|
||||
(travel "$1/$ITEM")
|
||||
elif [ ! -L "$ITEM" ]; then
|
||||
# Mount this file
|
||||
mktouch "$MOUNTINFO/$1/$ITEM" "$TRAVEL_ROOT"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
clone_dummy() {
|
||||
LINK=false
|
||||
in_list "$1" "$WHITELIST" && LINK=true
|
||||
|
||||
for ITEM in $MIRRDIR$1/* ; do
|
||||
REAL="${ITEM#$MIRRDIR}"
|
||||
if [ -d "$MOUNTINFO$REAL" ]; then
|
||||
# Need to clone deeper
|
||||
mkdir -p "$DUMMDIR$REAL"
|
||||
(clone_dummy "$REAL")
|
||||
elif [ ! -f "$DUMMDIR$REAL" ]; then
|
||||
# It's not the file to be added/replaced, clone it
|
||||
if [ -L "$ITEM" ]; then
|
||||
# Copy original symlink
|
||||
cp -afc "$ITEM" "$DUMMDIR$REAL"
|
||||
else
|
||||
if $LINK && [ ! -e "$MOUNTINFO$REAL" ]; then
|
||||
ln -s "$MIRRDIR$REAL" "$DUMMDIR$REAL"
|
||||
else
|
||||
if [ -d "$ITEM" ]; then
|
||||
mkdir -p "$DUMMDIR$REAL"
|
||||
else
|
||||
mktouch "$DUMMDIR$REAL"
|
||||
fi
|
||||
[ ! -e "$MOUNTINFO$REAL" ] && mktouch "$MOUNTINFO/mirror$REAL"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
bind_mount() {
|
||||
if [ -e "$1" -a -e "$2" ]; then
|
||||
mount -o bind "$1" "$2"
|
||||
if [ $? -eq 0 ]; then
|
||||
log_print "Mount: $1"
|
||||
else
|
||||
log_print "Mount Fail: $1"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
merge_image() {
|
||||
if [ -f $1 ]; then
|
||||
log_print "$1 found"
|
||||
if [ -f $IMG ]; then
|
||||
log_print "$IMG found, attempt to merge"
|
||||
|
||||
# Handle large images
|
||||
target_size_check $1
|
||||
MERGEUSED=$curUsedM
|
||||
target_size_check $IMG
|
||||
if [ "$MERGEUSED" -gt "$curFreeM" ]; then
|
||||
NEWDATASIZE=$((((MERGEUSED + curUsedM) / 32 + 2) * 32))
|
||||
log_print "Expanding $IMG to ${NEWDATASIZE}M..."
|
||||
resize2fs $IMG ${NEWDATASIZE}M
|
||||
fi
|
||||
|
||||
# Start merging
|
||||
mkdir /cache/data_img
|
||||
mkdir /cache/merge_img
|
||||
|
||||
# setup loop devices
|
||||
loopsetup $IMG
|
||||
LOOPDATA=$LOOPDEVICE
|
||||
log_print "$LOOPDATA $IMG"
|
||||
|
||||
loopsetup $1
|
||||
LOOPMERGE=$LOOPDEVICE
|
||||
log_print "$LOOPMERGE $1"
|
||||
|
||||
if [ ! -z $LOOPDATA -a ! -z $LOOPMERGE ]; then
|
||||
# if loop devices have been setup, mount images
|
||||
OK=false
|
||||
mount -t ext4 -o rw,noatime $LOOPDATA /cache/data_img && \
|
||||
mount -t ext4 -o rw,noatime $LOOPMERGE /cache/merge_img && \
|
||||
OK=true
|
||||
|
||||
if $OK; then
|
||||
# Merge (will reserve selinux contexts)
|
||||
cd /cache/merge_img
|
||||
for MOD in *; do
|
||||
if [ "$MOD" != "lost+found" ]; then
|
||||
log_print "Merging: $MOD"
|
||||
rm -rf /cache/data_img/$MOD
|
||||
fi
|
||||
done
|
||||
cp -afc . /cache/data_img
|
||||
log_print "Merge complete"
|
||||
cd /
|
||||
fi
|
||||
|
||||
umount /cache/data_img
|
||||
umount /cache/merge_img
|
||||
fi
|
||||
|
||||
losetup -d $LOOPDATA
|
||||
losetup -d $LOOPMERGE
|
||||
|
||||
rmdir /cache/data_img
|
||||
rmdir /cache/merge_img
|
||||
else
|
||||
log_print "Moving $1 to $IMG "
|
||||
mv $1 $IMG
|
||||
fi
|
||||
rm -f $1
|
||||
fi
|
||||
}
|
||||
|
||||
case $1 in
|
||||
post-fs )
|
||||
mv $LOGFILE /cache/last_magisk.log
|
||||
touch $LOGFILE
|
||||
chmod 644 $LOGFILE
|
||||
|
||||
# No more cache mods!
|
||||
# Only for multirom!
|
||||
|
||||
log_print "** Magisk post-fs mode running..."
|
||||
|
||||
# Cleanup legacy stuffs...
|
||||
rm -rf /cache/magisk /cache/magisk_merge /cache/magiskhide.log
|
||||
|
||||
[ -f $DISABLEFILE -o -f $UNINSTALLER ] && unblock
|
||||
|
||||
if [ -d /cache/magisk_mount ]; then
|
||||
log_print "* Mounting cache files"
|
||||
find /cache/magisk_mount -type f 2>/dev/null | while read ITEM ; do
|
||||
chmod 644 "$ITEM"
|
||||
chcon u:object_r:system_file:s0 "$ITEM"
|
||||
TARGET="${ITEM#/cache/magisk_mount}"
|
||||
bind_mount "$ITEM" "$TARGET"
|
||||
done
|
||||
fi
|
||||
|
||||
unblock
|
||||
;;
|
||||
|
||||
post-fs-data )
|
||||
# /data not mounted yet
|
||||
! mount | grep " /data " >/dev/null && unblock
|
||||
mount | grep " /data " | grep "tmpfs" >/dev/null && unblock
|
||||
|
||||
# Don't run twice
|
||||
if [ "`getprop magisk.restart_pfsd`" != "1" ]; then
|
||||
|
||||
log_print "** Magisk post-fs-data mode running..."
|
||||
|
||||
# Cache support
|
||||
mv /cache/stock_boot.img /data/stock_boot.img 2>/dev/null
|
||||
mv /cache/magisk.apk /data/magisk.apk 2>/dev/null
|
||||
mv /cache/custom_ramdisk_patch.sh /data/custom_ramdisk_patch.sh 2>/dev/null
|
||||
|
||||
if [ -d /cache/data_bin ]; then
|
||||
rm -rf $BINPATH
|
||||
mv /cache/data_bin $BINPATH
|
||||
fi
|
||||
|
||||
chmod -R 755 $BINPATH
|
||||
chown -R 0.0 $BINPATH
|
||||
|
||||
# Live patch sepolicy
|
||||
$BINPATH/sepolicy-inject --live
|
||||
|
||||
if [ -f $UNINSTALLER ]; then
|
||||
touch /dev/.magisk.unblock
|
||||
chcon u:object_r:device:s0 /dev/.magisk.unblock
|
||||
BOOTMODE=true sh $UNINSTALLER
|
||||
exit
|
||||
fi
|
||||
|
||||
# Set up environment
|
||||
mkdir -p $TOOLPATH
|
||||
$BINPATH/busybox --install -s $TOOLPATH
|
||||
ln -s $BINPATH/busybox $TOOLPATH/busybox
|
||||
# Prevent issues
|
||||
rm -f $TOOLPATH/su $TOOLPATH/sh $TOOLPATH/reboot
|
||||
chmod -R 755 $TOOLPATH
|
||||
chown -R 0.0 $TOOLPATH
|
||||
find $BINPATH $TOOLPATH -exec chcon -h u:object_r:system_file:s0 {} \;
|
||||
|
||||
# Multirom functions should go here, not available right now
|
||||
MULTIROM=false
|
||||
|
||||
# Image merging
|
||||
chmod 644 $IMG /cache/magisk.img /data/magisk_merge.img 2>/dev/null
|
||||
merge_image /cache/magisk.img
|
||||
merge_image /data/magisk_merge.img
|
||||
|
||||
# Mount magisk.img
|
||||
[ ! -d $MOUNTPOINT ] && mkdir -p $MOUNTPOINT
|
||||
if ! mount | grep $MOUNTPOINT; then
|
||||
loopsetup $IMG
|
||||
[ ! -z $LOOPDEVICE ] && mount -t ext4 -o rw,noatime $LOOPDEVICE $MOUNTPOINT
|
||||
if [ $? -ne 0 ]; then
|
||||
log_print "magisk.img mount failed, nothing to do :("
|
||||
unblock
|
||||
fi
|
||||
fi
|
||||
|
||||
# Remove empty directories, legacy paths, symlinks, old temporary images
|
||||
find $MOUNTPOINT -type d -depth ! -path "*core*" -exec rmdir {} \; 2>/dev/null
|
||||
rm -rf $MOUNTPOINT/zzsupersu $MOUNTPOINT/phh $COREDIR/bin $COREDIR/dummy $COREDIR/mirror \
|
||||
$COREDIR/busybox /data/magisk/*.img /data/busybox 2>/dev/null
|
||||
|
||||
# Remove modules that are labeled to be removed
|
||||
for MOD in $MOUNTPOINT/* ; do
|
||||
rm -f $MOD/system/placeholder 2>/dev/null
|
||||
if [ -f $MOD/remove ]; then
|
||||
log_print "Remove module: $MOD"
|
||||
rm -rf $MOD
|
||||
fi
|
||||
done
|
||||
|
||||
# Unmount, shrink, remount
|
||||
if umount $MOUNTPOINT; then
|
||||
losetup -d $LOOPDEVICE 2>/dev/null
|
||||
target_size_check $IMG
|
||||
NEWDATASIZE=$(((curUsedM / 32 + 2) * 32))
|
||||
if [ "$curSizeM" -gt "$NEWDATASIZE" ]; then
|
||||
log_print "Shrinking $IMG to ${NEWDATASIZE}M..."
|
||||
resize2fs $IMG ${NEWDATASIZE}M
|
||||
fi
|
||||
loopsetup $IMG
|
||||
[ ! -z $LOOPDEVICE ] && mount -t ext4 -o rw,noatime $LOOPDEVICE $MOUNTPOINT
|
||||
if [ $? -ne 0 ]; then
|
||||
log_print "magisk.img mount failed, nothing to do :("
|
||||
unblock
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start MagiskSU if no SuperSU
|
||||
export PATH=$OLDPATH
|
||||
[ ! -f /sbin/launch_daemonsu.sh ] && sh $COREDIR/su/magisksu.sh
|
||||
export PATH=$TOOLPATH:$OLDPATH
|
||||
|
||||
[ -f $DISABLEFILE ] && unblock
|
||||
|
||||
log_print "* Preparing modules"
|
||||
|
||||
mkdir -p $DUMMDIR
|
||||
mkdir -p $MIRRDIR/system
|
||||
|
||||
# Remove crap folder
|
||||
rm -rf $MOUNTPOINT/lost+found
|
||||
|
||||
# Link vendor if not exist
|
||||
if [ ! -e /vendor ]; then
|
||||
mount -o rw,remount rootfs /
|
||||
ln -s /system/vendor /vendor
|
||||
mount -o ro,remount rootfs /
|
||||
fi
|
||||
|
||||
# Travel through all mods
|
||||
for MOD in $MOUNTPOINT/* ; do
|
||||
if [ -f $MOD/auto_mount -a -d $MOD/system -a ! -f $MOD/disable ]; then
|
||||
TRAVEL_ROOT=$MOD
|
||||
(travel system)
|
||||
rm -f $MOD/vendor 2>/dev/null
|
||||
if [ -d $MOD/system/vendor ]; then
|
||||
ln -s $MOD/system/vendor $MOD/vendor
|
||||
(travel vendor)
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Proper permissions for generated items
|
||||
find $TMPDIR -exec chcon -h u:object_r:system_file:s0 {} \;
|
||||
|
||||
# linker(64), t*box required for bin
|
||||
if [ -f $MOUNTINFO/dummy/system/bin ]; then
|
||||
cp -afc /system/bin/linker* /system/bin/t*box $DUMMDIR/system/bin/
|
||||
fi
|
||||
|
||||
# Start doing tasks
|
||||
|
||||
# Stage 1
|
||||
log_print "* Stage 1: Mount system and vendor mirrors"
|
||||
SYSTEMBLOCK=`mount | grep " /system " | awk '{print $1}'`
|
||||
mkdir -p $MIRRDIR/system
|
||||
mount -o ro $SYSTEMBLOCK $MIRRDIR/system
|
||||
if [ `mount | grep -c " /vendor "` -ne 0 ]; then
|
||||
VENDORBLOCK=`mount | grep " /vendor " | awk '{print $1}'`
|
||||
mkdir -p $MIRRDIR/vendor
|
||||
mount -o ro $VENDORBLOCK $MIRRDIR/vendor
|
||||
else
|
||||
ln -s $MIRRDIR/system/vendor $MIRRDIR/vendor
|
||||
fi
|
||||
|
||||
# Since mirrors always exist, we load libraries and binaries from mirrors
|
||||
export LD_LIBRARY_PATH=$MIRRDIR/system/lib:$MIRRDIR/vendor/lib
|
||||
[ -d $MIRRDIR/system/lib64 ] && export LD_LIBRARY_PATH=$MIRRDIR/system/lib64:$MIRRDIR/vendor/lib64
|
||||
|
||||
# Stage 2
|
||||
log_print "* Stage 2: Mount dummy skeletons"
|
||||
# Move /system/vendor to /vendor for consistency
|
||||
mv -f $MOUNTINFO/dummy/system/vendor $MOUNTINFO/dummy/vendor 2>/dev/null
|
||||
mv -f $DUMMDIR/system/vendor $DUMMDIR/vendor 2>/dev/null
|
||||
find $MOUNTINFO/dummy -type f 2>/dev/null | while read ITEM ; do
|
||||
TARGET="${ITEM#$MOUNTINFO/dummy}"
|
||||
ORIG="$DUMMDIR$TARGET"
|
||||
(clone_dummy "$TARGET")
|
||||
bind_mount "$ORIG" "$TARGET"
|
||||
done
|
||||
|
||||
# Check if the dummy /system/bin is empty, it shouldn't
|
||||
[ -e $DUMMDIR/system/bin -a ! -e $DUMMDIR/system/bin/sh ] && clone_dummy /system/bin
|
||||
|
||||
# Stage 3
|
||||
log_print "* Stage 3: Mount module items"
|
||||
find $MOUNTINFO/system $MOUNTINFO/vendor -type f 2>/dev/null | while read ITEM ; do
|
||||
TARGET="${ITEM#$MOUNTINFO}"
|
||||
ORIG="`cat "$ITEM"`$TARGET"
|
||||
bind_mount "$ORIG" "$TARGET"
|
||||
done
|
||||
|
||||
# Stage 4
|
||||
log_print "* Stage 4: Execute scripts"
|
||||
run_scripts post-fs-data
|
||||
|
||||
# Stage 5
|
||||
log_print "* Stage 5: Mount mirrored items back to dummy"
|
||||
find $MOUNTINFO/mirror -type f 2>/dev/null | while read ITEM ; do
|
||||
TARGET="${ITEM#$MOUNTINFO/mirror}"
|
||||
ORIG="$MIRRDIR$TARGET"
|
||||
bind_mount "$ORIG" "$TARGET"
|
||||
done
|
||||
|
||||
# Bind hosts for Adblock apps
|
||||
if [ -f $COREDIR/hosts ]; then
|
||||
log_print "* Enabling systemless hosts file support"
|
||||
bind_mount $COREDIR/hosts /system/etc/hosts
|
||||
fi
|
||||
|
||||
if [ -f /data/magisk.apk ]; then
|
||||
if [ -z `ls /data/app | grep com.topjohnwu.magisk` ]; then
|
||||
mkdir /data/app/com.topjohnwu.magisk-1
|
||||
cp /data/magisk.apk /data/app/com.topjohnwu.magisk-1/base.apk
|
||||
chown 1000.1000 /data/app/com.topjohnwu.magisk-1
|
||||
chown 1000.1000 /data/app/com.topjohnwu.magisk-1/base.apk
|
||||
chmod 755 /data/app/com.topjohnwu.magisk-1
|
||||
chmod 644 /data/app/com.topjohnwu.magisk-1/base.apk
|
||||
chcon u:object_r:apk_data_file:s0 /data/app/com.topjohnwu.magisk-1
|
||||
chcon u:object_r:apk_data_file:s0 /data/app/com.topjohnwu.magisk-1/base.apk
|
||||
fi
|
||||
rm -f /data/magisk.apk 2>/dev/null
|
||||
fi
|
||||
|
||||
for MOD in $MOUNTPOINT/* ; do
|
||||
# Read in defined system props
|
||||
if [ -f $MOD/system.prop ]; then
|
||||
log_print "* Reading props from $MOD/system.prop"
|
||||
/data/magisk/resetprop --file $MOD/system.prop
|
||||
fi
|
||||
done
|
||||
|
||||
# Expose busybox
|
||||
[ "`getprop persist.magisk.busybox`" = "1" ] && sh /sbin/magic_mask.sh mount_busybox
|
||||
|
||||
# Restart post-fs-data if necessary (multirom)
|
||||
$MULTIROM && setprop magisk.restart_pfsd 1
|
||||
|
||||
fi
|
||||
unblock
|
||||
;;
|
||||
|
||||
mount_busybox )
|
||||
log_print "* Enabling BusyBox"
|
||||
cp -afc /system/xbin/. $TOOLPATH
|
||||
umount /system/xbin 2>/dev/null
|
||||
bind_mount $TOOLPATH /system/xbin
|
||||
;;
|
||||
|
||||
service )
|
||||
# Version info
|
||||
MAGISK_VERSION_STUB
|
||||
log_print "** Magisk late_start service mode running..."
|
||||
if [ -f $DISABLEFILE ]; then
|
||||
setprop ro.magisk.disable 1
|
||||
exit
|
||||
fi
|
||||
run_scripts service
|
||||
;;
|
||||
|
||||
esac
|
||||
@@ -1,150 +1,153 @@
|
||||
#!/system/bin/sh
|
||||
##########################################################################################
|
||||
#
|
||||
# Magisk Uninstaller
|
||||
# by topjohnwu
|
||||
#
|
||||
# This script can be placed in /cache/magisk_uninstaller.sh
|
||||
# The Magisk main binary will pick up the script, and uninstall itself, following a reboot
|
||||
# This script can also be used in flashable zip with the uninstaller_loader.sh
|
||||
#
|
||||
# This script will try to do restoration with the following:
|
||||
# 1-1. Find and restore the original stock boot image dump (OTA proof)
|
||||
# 1-2. If 1-1 fails, restore ramdisk from the internal backup
|
||||
# (ramdisk fully restored, not OTA friendly)
|
||||
# 1-3. If 1-2 fails, it will remove added files in ramdisk, however modified files
|
||||
# are remained modified, because we have no backups. By doing so, Magisk will
|
||||
# not be started at boot, but this isn't actually 100% cleaned up
|
||||
# 2. Remove all Magisk related files
|
||||
# (The list is LARGE, most likely due to bad decision in early versions
|
||||
# the latest versions has much less bloat to cleanup)
|
||||
#
|
||||
##########################################################################################
|
||||
|
||||
[ -z $BOOTMODE ] && BOOTMODE=false
|
||||
TMPDIR=/tmp
|
||||
($BOOTMODE) && TMPDIR=/dev/tmp
|
||||
|
||||
BINDIR=/data/magisk
|
||||
CHROMEDIR=$BINDIR/chromeos
|
||||
|
||||
NEWBOOT=$TMPDIR/boottmp/new-boot.img
|
||||
UNPACKDIR=$TMPDIR/boottmp/bootunpack
|
||||
RAMDISK=$TMPDIR/boottmp/ramdisk
|
||||
|
||||
SYSTEMLIB=/system/lib
|
||||
[ -d /system/lib64 ] && SYSTEMLIB=/system/lib64
|
||||
|
||||
ui_print() {
|
||||
echo "$1"
|
||||
# Call ui_print_wrap if exists, or else simply use echo
|
||||
# Useful when wrapped in flashable zip
|
||||
ui_print_wrap() {
|
||||
type ui_print >/dev/null 2>&1 && ui_print "$1" || echo "$1"
|
||||
}
|
||||
|
||||
grep_prop() {
|
||||
REGEX="s/^$1=//p"
|
||||
shift
|
||||
FILES=$@
|
||||
if [ -z "$FILES" ]; then
|
||||
FILES='/system/build.prop'
|
||||
fi
|
||||
cat $FILES 2>/dev/null | sed -n $REGEX | head -n 1
|
||||
}
|
||||
|
||||
find_boot_image() {
|
||||
if [ -z "$BOOTIMAGE" ]; then
|
||||
for PARTITION in kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do
|
||||
BOOTIMAGE=`readlink /dev/block/by-name/$PARTITION || readlink /dev/block/platform/*/by-name/$PARTITION || readlink /dev/block/platform/*/*/by-name/$PARTITION`
|
||||
if [ ! -z "$BOOTIMAGE" ]; then break; fi
|
||||
done
|
||||
fi
|
||||
if [ -z "$BOOTIMAGE" ]; then
|
||||
FSTAB="/etc/recovery.fstab"
|
||||
[ ! -f "$FSTAB" ] && FSTAB="/etc/recovery.fstab.bak"
|
||||
[ -f "$FSTAB" ] && BOOTIMAGE=`grep -E '\b/boot\b' "$FSTAB" | grep -oE '/dev/[a-zA-Z0-9_./-]*'`
|
||||
# Call abort if exists, or else show error message and exit
|
||||
# Essential when wrapped in flashable zip
|
||||
abort_wrap() {
|
||||
type abort >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
ui_print_wrap "$1"
|
||||
exit 1
|
||||
else
|
||||
abort "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
unpack_boot() {
|
||||
rm -rf $UNPACKDIR $RAMDISK 2>/dev/null
|
||||
mkdir -p $UNPACKDIR
|
||||
mkdir -p $RAMDISK
|
||||
cd $UNPACKDIR
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB $BINDIR/bootimgtools --extract $1
|
||||
|
||||
cd $RAMDISK
|
||||
$BINDIR/busybox gunzip -c < $UNPACKDIR/ramdisk.gz | cpio -i
|
||||
}
|
||||
|
||||
repack_boot() {
|
||||
cd $RAMDISK
|
||||
find . | cpio -o -H newc 2>/dev/null | gzip -9 > $UNPACKDIR/ramdisk.gz
|
||||
cd $UNPACKDIR
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB $BINDIR/bootimgtools --repack $BOOTIMAGE
|
||||
if [ -f chromeos ]; then
|
||||
echo " " > config
|
||||
echo " " > bootloader
|
||||
LD_LIBRARY_PATH=$SYSTEMLIB $CHROMEDIR/futility vbutil_kernel --pack new-boot.img.signed --keyblock $CHROMEDIR/kernel.keyblock --signprivate $CHROMEDIR/kernel_data_key.vbprivk --version 1 --vmlinuz new-boot.img --config config --arch arm --bootloader bootloader --flags 0x1
|
||||
rm -f new-boot.img
|
||||
mv new-boot.img.signed new-boot.img
|
||||
fi
|
||||
if ($SAMSUNG); then
|
||||
SAMSUNG_CHECK=$(cat new-boot.img | grep SEANDROIDENFORCE)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -n "SEANDROIDENFORCE" >> new-boot.img
|
||||
fi
|
||||
fi
|
||||
if ($LGE_G); then
|
||||
# Prevent secure boot error on LG G2/G3.
|
||||
# Just for know, It's a pattern which bootloader verifies at boot. Thanks to LG hackers.
|
||||
echo -n -e "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79" >> new-boot.img
|
||||
fi
|
||||
mv new-boot.img $NEWBOOT
|
||||
}
|
||||
|
||||
# Set permissions
|
||||
chmod -R 755 $CHROMEDIR/futility $BINDIR
|
||||
|
||||
# Find the boot image
|
||||
find_boot_image
|
||||
if [ -z "$BOOTIMAGE" ]; then
|
||||
ui_print "! Unable to detect boot image"
|
||||
if [ ! -d $MAGISKBIN -o ! -f $MAGISKBIN/magiskboot -o ! -f $MAGISKBIN/util_functions.sh ]; then
|
||||
ui_print_wrap "! Cannot find $MAGISKBIN"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ui_print "- Found Boot Image: $BOOTIMAGE"
|
||||
[ -z $BOOTMODE ] && BOOTMODE=false
|
||||
|
||||
# Detect special vendors
|
||||
SAMSUNG=false
|
||||
SAMSUNG_CHECK=$(cat /system/build.prop | grep "ro.build.fingerprint=" | grep -i "samsung")
|
||||
if [ $? -eq 0 ]; then
|
||||
SAMSUNG=true
|
||||
fi
|
||||
LGE_G=false
|
||||
RBRAND=$(grep_prop ro.product.brand)
|
||||
RMODEL=$(grep_prop ro.product.device)
|
||||
if [ "$RBRAND" = "lge" ] || [ "$RBRAND" = "LGE" ]; then
|
||||
if [ "$RMODEL" = "*D80*" ] ||
|
||||
[ "$RMODEL" = "*S98*" ] ||
|
||||
[ "$RMODEL" = "*D85*" ] ||
|
||||
[ "$RMODEL" = "*F40*" ]; then
|
||||
LGE_G=true
|
||||
ui_print "! Bump device detected"
|
||||
fi
|
||||
MAGISKBIN=/data/magisk
|
||||
CHROMEDIR=$MAGISKBIN/chromeos
|
||||
|
||||
# Default permissions
|
||||
umask 022
|
||||
|
||||
# Load utility functions
|
||||
. $MAGISKBIN/util_functions.sh
|
||||
|
||||
# Find the boot image
|
||||
find_boot_image
|
||||
[ -z $BOOTIMAGE ] && abort "! Unable to detect boot image"
|
||||
|
||||
ui_print_wrap "- Found Boot Image: $BOOTIMAGE"
|
||||
|
||||
cd $MAGISKBIN
|
||||
|
||||
ui_print_wrap "- Unpacking boot image"
|
||||
./magiskboot --unpack "$BOOTIMAGE"
|
||||
CHROMEOS=false
|
||||
case $? in
|
||||
1 )
|
||||
abort_wrap "! Unable to unpack boot image"
|
||||
;;
|
||||
2 )
|
||||
CHROMEOS=true
|
||||
;;
|
||||
3 )
|
||||
ui_print_wrap "! Sony ELF32 format detected"
|
||||
abort_wrap "! Please use BootBridge from @AdrianDC to flash Magisk"
|
||||
;;
|
||||
4 )
|
||||
ui_print_wrap "! Sony ELF64 format detected"
|
||||
abort_wrap "! Stock kernel cannot be patched, please use a custom kernel"
|
||||
esac
|
||||
|
||||
# Update our previous backup to new format if exists
|
||||
if [ -f /data/stock_boot.img ]; then
|
||||
SHA1=`./magiskboot --sha1 /data/stock_boot.img | tail -n 1`
|
||||
STOCKDUMP=/data/stock_boot_${SHA1}.img
|
||||
mv /data/stock_boot.img $STOCKDUMP
|
||||
./magiskboot --compress $STOCKDUMP
|
||||
fi
|
||||
|
||||
# First unpack the boot image
|
||||
unpack_boot $BOOTIMAGE
|
||||
|
||||
SUPERSU=false
|
||||
[ -f sbin/launch_daemonsu.sh ] && SUPERSU=true
|
||||
|
||||
if ($SUPERSU); then
|
||||
ui_print "- SuperSU patched image detected"
|
||||
rm -f magisk sbin/init.magisk.rc sbin/magic_mask.sh
|
||||
repack_boot
|
||||
else
|
||||
if [ -f /data/stock_boot.img ]; then
|
||||
ui_print "- Boot image backup found!"
|
||||
NEWBOOT=/data/stock_boot.img
|
||||
else
|
||||
ui_print "! Boot image backup unavailable"
|
||||
if [ -d ".backup" ]; then
|
||||
ui_print "- Restoring ramdisk with backup"
|
||||
cp -af .backup/. .
|
||||
# Detect boot image state
|
||||
./magiskboot --cpio-test ramdisk.cpio
|
||||
case $? in
|
||||
0 ) # Stock boot
|
||||
ui_print_wrap "- Stock boot image detected!"
|
||||
ui_print_wrap "! Magisk is not installed!"
|
||||
exit
|
||||
;;
|
||||
1 ) # Magisk patched
|
||||
ui_print_wrap "- Magisk patched image detected!"
|
||||
# Find SHA1 of stock boot image
|
||||
if [ -z $SHA1 ]; then
|
||||
./magiskboot --cpio-extract ramdisk.cpio init.magisk.rc init.magisk.rc.old
|
||||
SHA1=`grep_prop "# STOCKSHA1" init.magisk.rc.old`
|
||||
rm -f init.magisk.rc.old
|
||||
fi
|
||||
rm -f magisk sbin/init.magisk.rc sbin/magic_mask.sh
|
||||
repack_boot
|
||||
fi
|
||||
[ ! -z $SHA1 ] && STOCKDUMP=/data/stock_boot_${SHA1}.img
|
||||
if [ -f ${STOCKDUMP}.gz ]; then
|
||||
ui_print_wrap "- Boot image backup found!"
|
||||
./magiskboot --decompress ${STOCKDUMP}.gz stock_boot.img
|
||||
else
|
||||
ui_print_wrap "! Boot image backup unavailable"
|
||||
ui_print_wrap "- Restoring ramdisk with backup"
|
||||
./magiskboot --cpio-restore ramdisk.cpio
|
||||
./magiskboot --repack $BOOTIMAGE stock_boot.img
|
||||
fi
|
||||
;;
|
||||
2 ) # Other patched
|
||||
ui_print_wrap "! Boot image patched by other programs!"
|
||||
abort_wrap "! Cannot uninstall with this uninstaller"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Sign chromeos boot
|
||||
if $CHROMEOS; then
|
||||
echo > empty
|
||||
|
||||
./chromeos/futility vbutil_kernel --pack stock_boot.img.signed \
|
||||
--keyblock ./chromeos/kernel.keyblock --signprivate ./chromeos/kernel_data_key.vbprivk \
|
||||
--version 1 --vmlinuz stock_boot.img --config empty --arch arm --bootloader empty --flags 0x1
|
||||
|
||||
rm -f empty stock_boot.img
|
||||
mv stock_boot.img.signed stock_boot.img
|
||||
fi
|
||||
|
||||
chmod 644 $NEWBOOT
|
||||
ui_print_wrap "- Flashing stock/reverted image"
|
||||
if [ -L "$BOOTIMAGE" ]; then
|
||||
dd if=stock_boot.img of="$BOOTIMAGE" bs=4096
|
||||
else
|
||||
cat stock_boot.img /dev/zero | dd of="$BOOTIMAGE" bs=4096 >/dev/null 2>&1
|
||||
fi
|
||||
rm -f stock_boot.img
|
||||
|
||||
ui_print "- Flashing stock/reverted image"
|
||||
[ ! -L "$BOOTIMAGE" ] && dd if=/dev/zero of=$BOOTIMAGE bs=4096 2>/dev/null
|
||||
dd if=$NEWBOOT of=$BOOTIMAGE bs=4096
|
||||
|
||||
ui_print "- Removing Magisk files"
|
||||
ui_print_wrap "- Removing Magisk files"
|
||||
rm -rf /cache/magisk.log /cache/last_magisk.log /cache/magiskhide.log /cache/.disable_magisk \
|
||||
/cache/magisk /cache/magisk_merge /cache/magisk_mount /cache/unblock /cache/magisk_uninstaller.sh \
|
||||
/data/Magisk.apk /data/magisk.apk /data/magisk.img /data/magisk_merge.img \
|
||||
/data/busybox /data/magisk /data/custom_ramdisk_patch.sh 2>/dev/null
|
||||
/data/Magisk.apk /data/magisk.apk /data/magisk.img /data/magisk_merge.img /data/magisk_debug.log \
|
||||
/data/busybox /data/magisk /data/custom_ramdisk_patch.sh /data/property/*magisk*
|
||||
/data/app/com.topjohnwu.magisk* /data/user/*/com.topjohnwu.magisk 2>/dev/null
|
||||
|
||||
($BOOTMODE) && reboot
|
||||
$BOOTMODE && reboot
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
MODDIR=${0%/*}
|
||||
LOGFILE=/cache/magisk.log
|
||||
|
||||
log_print() {
|
||||
echo "MagiskSU: $1"
|
||||
echo "MagiskSU: $1" >> $LOGFILE
|
||||
log -p i -t Magisk "MagiskSU: $1"
|
||||
}
|
||||
|
||||
log_print "Moving and linking /sbin binaries"
|
||||
mount -o rw,remount rootfs /
|
||||
cp -af /sbin /sbin_orig
|
||||
mount -o ro,remount rootfs /
|
||||
|
||||
log_print "Exposing su binary"
|
||||
rm -rf /magisk/.core/bin $MODDIR/sbin_bind
|
||||
mkdir -p $MODDIR/sbin_bind
|
||||
ln -s /sbin_orig/* $MODDIR/sbin_bind
|
||||
chcon -h u:object_r:rootfs:s0 $MODDIR/sbin_bind/*
|
||||
chmod 755 $MODDIR/sbin_bind
|
||||
ln -s $MODDIR/su $MODDIR/sbin_bind/su
|
||||
ln -s /data/magisk/sepolicy-inject $MODDIR/sbin_bind/sepolicy-inject
|
||||
mount -o bind $MODDIR/sbin_bind /sbin
|
||||
|
||||
log_print "Starting su daemon"
|
||||
/sbin/su --daemon
|
||||
89
scripts/uninstaller_loader.sh
Normal file
89
scripts/uninstaller_loader.sh
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/sbin/sh
|
||||
##########################################################################################
|
||||
#
|
||||
# Magisk Uninstaller (used in recovery)
|
||||
# by topjohnwu
|
||||
#
|
||||
# This script will load the real uninstaller in a flashable zip
|
||||
#
|
||||
##########################################################################################
|
||||
|
||||
##########################################################################################
|
||||
# Preparation
|
||||
##########################################################################################
|
||||
|
||||
BOOTMODE=false
|
||||
INSTALLER=/tmp/uninstall
|
||||
# Default permissions
|
||||
umask 022
|
||||
|
||||
OUTFD=$2
|
||||
ZIP=$3
|
||||
|
||||
rm -rf $INSTALLER 2>/dev/null
|
||||
mkdir -p $INSTALLER
|
||||
unzip -o "$ZIP" -d $INSTALLER 2>/dev/null
|
||||
|
||||
if [ ! -f $INSTALLER/util_functions.sh ]; then
|
||||
echo "! Failed: Unable to extract zip file!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Load utility functions
|
||||
. $INSTALLER/util_functions.sh
|
||||
|
||||
get_outfd
|
||||
|
||||
##########################################################################################
|
||||
# Main
|
||||
##########################################################################################
|
||||
|
||||
ui_print "************************"
|
||||
ui_print " Magisk Uninstaller "
|
||||
ui_print "************************"
|
||||
|
||||
ui_print "- Mounting /system, /vendor, /cache, /data"
|
||||
mount -o ro /system 2>/dev/null
|
||||
mount -o ro /vendor 2>/dev/null
|
||||
mount /cache 2>/dev/null
|
||||
mount /data 2>/dev/null
|
||||
|
||||
[ -f /system/build.prop ] || abort "! /system could not be mounted!"
|
||||
|
||||
api_level_arch_detect
|
||||
|
||||
ui_print "- Device platform: $ARCH"
|
||||
CHROMEDIR=$INSTALLER/chromeos
|
||||
BINDIR=$INSTALLER/$ARCH
|
||||
|
||||
##########################################################################################
|
||||
# Detection all done, start installing
|
||||
##########################################################################################
|
||||
|
||||
MAGISKBIN=/data/magisk
|
||||
|
||||
if is_mounted /data; then
|
||||
# Copy the binaries to /data/magisk, in case they do not exist
|
||||
rm -rf $MAGISKBIN 2>/dev/null
|
||||
mkdir -p $MAGISKBIN
|
||||
cp -af $BINDIR/. $MAGISKBIN
|
||||
cp -af $CHROMEDIR $MAGISKBIN
|
||||
cp -af $INSTALLER/util_functions.sh $MAGISKBIN
|
||||
chmod -R 755 $MAGISKBIN
|
||||
# Run the acttual uninstallation
|
||||
recovery_actions
|
||||
. $INSTALLER/magisk_uninstaller.sh
|
||||
recovery_cleanup
|
||||
else
|
||||
ui_print "! Data unavailable"
|
||||
ui_print "! Placing uninstall script to /cache"
|
||||
ui_print "! The device might reboot multiple times"
|
||||
cp -af $INSTALLER/magisk_uninstaller.sh /cache/magisk_uninstaller.sh
|
||||
umount /system
|
||||
ui_print "- Rebooting....."
|
||||
sleep 5
|
||||
reboot
|
||||
fi
|
||||
|
||||
ui_print "- Done"
|
||||
exit 0
|
||||
194
scripts/util_functions.sh
Normal file
194
scripts/util_functions.sh
Normal file
@@ -0,0 +1,194 @@
|
||||
##########################################################################################
|
||||
#
|
||||
# Magisk General Utility Functions
|
||||
# by topjohnwu
|
||||
#
|
||||
# Used in flash_script.sh, addon.d.sh, magisk module installers, and uninstaller
|
||||
#
|
||||
##########################################################################################
|
||||
|
||||
MAGISK_VERSION_STUB
|
||||
|
||||
get_outfd() {
|
||||
readlink /proc/$$/fd/$OUTFD 2>/dev/null | grep /tmp >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
OUTFD=0
|
||||
|
||||
for FD in `ls /proc/$$/fd`; do
|
||||
readlink /proc/$$/fd/$FD 2>/dev/null | grep pipe >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
ps | grep " 3 $FD " | grep -v grep >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
OUTFD=$FD
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
ui_print() {
|
||||
if $BOOTMODE; then
|
||||
echo "$1"
|
||||
else
|
||||
echo -n -e "ui_print $1\n" >> /proc/self/fd/$OUTFD
|
||||
echo -n -e "ui_print\n" >> /proc/self/fd/$OUTFD
|
||||
fi
|
||||
}
|
||||
|
||||
getvar() {
|
||||
local VARNAME=$1
|
||||
local VALUE=$(eval echo \$"$VARNAME");
|
||||
for FILE in /dev/.magisk /data/.magisk /cache/.magisk /system/.magisk; do
|
||||
if [ -z "$VALUE" ]; then
|
||||
LINE=$(cat $FILE 2>/dev/null | grep "$VARNAME=")
|
||||
if [ ! -z "$LINE" ]; then
|
||||
VALUE=${LINE#*=}
|
||||
fi
|
||||
fi
|
||||
done
|
||||
eval $VARNAME=\$VALUE
|
||||
}
|
||||
|
||||
find_boot_image() {
|
||||
if [ -z "$BOOTIMAGE" ]; then
|
||||
for BLOCK in boot_a BOOT_A kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do
|
||||
BOOTIMAGE=`ls /dev/block/by-name/$BLOCK || ls /dev/block/platform/*/by-name/$BLOCK || ls /dev/block/platform/*/*/by-name/$BLOCK` 2>/dev/null
|
||||
[ ! -z $BOOTIMAGE ] && break
|
||||
done
|
||||
fi
|
||||
# Recovery fallback
|
||||
if [ -z "$BOOTIMAGE" ]; then
|
||||
for FSTAB in /etc/*fstab*; do
|
||||
BOOTIMAGE=`grep -E '\b/boot\b' $FSTAB | grep -v "#" | grep -oE '/dev/[a-zA-Z0-9_./-]*'`
|
||||
[ ! -z $BOOTIMAGE ] && break
|
||||
done
|
||||
fi
|
||||
[ -L "$BOOTIMAGE" ] && BOOTIMAGE=`readlink $BOOTIMAGE`
|
||||
}
|
||||
|
||||
is_mounted() {
|
||||
if [ ! -z "$2" ]; then
|
||||
cat /proc/mounts | grep $1 | grep $2, >/dev/null
|
||||
else
|
||||
cat /proc/mounts | grep $1 >/dev/null
|
||||
fi
|
||||
return $?
|
||||
}
|
||||
|
||||
grep_prop() {
|
||||
REGEX="s/^$1=//p"
|
||||
shift
|
||||
FILES=$@
|
||||
if [ -z "$FILES" ]; then
|
||||
FILES='/system/build.prop'
|
||||
fi
|
||||
cat $FILES 2>/dev/null | sed -n "$REGEX" | head -n 1
|
||||
}
|
||||
|
||||
remove_system_su() {
|
||||
if [ -f /system/bin/su -o -f /system/xbin/su ] && [ ! -f /su/bin/su ]; then
|
||||
ui_print "! System installed root detected, mount rw :("
|
||||
mount -o rw,remount /system
|
||||
# SuperSU
|
||||
if [ -e /system/bin/.ext/.su ]; then
|
||||
mv -f /system/bin/app_process32_original /system/bin/app_process32 2>/dev/null
|
||||
mv -f /system/bin/app_process64_original /system/bin/app_process64 2>/dev/null
|
||||
mv -f /system/bin/install-recovery_original.sh /system/bin/install-recovery.sh 2>/dev/null
|
||||
cd /system/bin
|
||||
if [ -e app_process64 ]; then
|
||||
ln -sf app_process64 app_process
|
||||
else
|
||||
ln -sf app_process32 app_process
|
||||
fi
|
||||
fi
|
||||
rm -rf /system/.pin /system/bin/.ext /system/etc/.installed_su_daemon /system/etc/.has_su_daemon \
|
||||
/system/xbin/daemonsu /system/xbin/su /system/xbin/sugote /system/xbin/sugote-mksh /system/xbin/supolicy \
|
||||
/system/bin/app_process_init /system/bin/su /cache/su /system/lib/libsupol.so /system/lib64/libsupol.so \
|
||||
/system/su.d /system/etc/install-recovery.sh /system/etc/init.d/99SuperSUDaemon /cache/install-recovery.sh \
|
||||
/system/.supersu /cache/.supersu /data/.supersu \
|
||||
/system/app/Superuser.apk /system/app/SuperSU /cache/Superuser.apk 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
api_level_arch_detect() {
|
||||
API=`grep_prop ro.build.version.sdk`
|
||||
ABI=`grep_prop ro.product.cpu.abi | cut -c-3`
|
||||
ABI2=`grep_prop ro.product.cpu.abi2 | cut -c-3`
|
||||
ABILONG=`grep_prop ro.product.cpu.abi`
|
||||
|
||||
ARCH=arm
|
||||
IS64BIT=false
|
||||
if [ "$ABI" = "x86" ]; then ARCH=x86; fi;
|
||||
if [ "$ABI2" = "x86" ]; then ARCH=x86; fi;
|
||||
if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=true; fi;
|
||||
if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=true; fi;
|
||||
}
|
||||
|
||||
recovery_actions() {
|
||||
# TWRP bug fix
|
||||
mount -o bind /dev/urandom /dev/random
|
||||
# Temporarily block out all custom recovery binaries/libs
|
||||
mv /sbin /sbin_tmp
|
||||
# Add all possible library paths
|
||||
OLD_LD_PATH=$LD_LIBRARY_PATH
|
||||
$IS64BIT && export LD_LIBRARY_PATH=/system/lib64:/system/vendor/lib64 || export LD_LIBRARY_PATH=/system/lib:/system/vendor/lib
|
||||
}
|
||||
|
||||
recovery_cleanup() {
|
||||
mv /sbin_tmp /sbin
|
||||
# Clear LD_LIBRARY_PATH
|
||||
export LD_LIBRARY_PATH=$OLD_LD_PATH
|
||||
ui_print "- Unmounting partitions"
|
||||
umount -l /system
|
||||
umount -l /vendor 2>/dev/null
|
||||
umount -l /dev/random
|
||||
}
|
||||
|
||||
abort() {
|
||||
ui_print "$1"
|
||||
mv /sbin_tmp /sbin 2>/dev/null
|
||||
exit 1
|
||||
}
|
||||
|
||||
set_perm() {
|
||||
chown $2:$3 $1 || exit 1
|
||||
chmod $4 $1 || exit 1
|
||||
if [ ! -z $5 ]; then
|
||||
chcon $5 $1 2>/dev/null
|
||||
else
|
||||
chcon 'u:object_r:system_file:s0' $1 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
set_perm_recursive() {
|
||||
find $1 -type d 2>/dev/null | while read dir; do
|
||||
set_perm $dir $2 $3 $4 $6
|
||||
done
|
||||
find $1 -type f 2>/dev/null | while read file; do
|
||||
set_perm $file $2 $3 $5 $6
|
||||
done
|
||||
}
|
||||
|
||||
mktouch() {
|
||||
mkdir -p ${1%/*}
|
||||
if [ -z "$2" ]; then
|
||||
touch $1
|
||||
else
|
||||
echo $2 > $1
|
||||
fi
|
||||
chmod 644 $1
|
||||
}
|
||||
|
||||
request_size_check() {
|
||||
reqSizeM=`du -s $1 | cut -f1`
|
||||
reqSizeM=$((reqSizeM / 1024 + 1))
|
||||
}
|
||||
|
||||
image_size_check() {
|
||||
SIZE="`$MAGISKBIN/magisk --imgsize $IMG`"
|
||||
curUsedM=`echo "$SIZE" | cut -d" " -f1`
|
||||
curSizeM=`echo "$SIZE" | cut -d" " -f2`
|
||||
curFreeM=$((curSizeM - curUsedM))
|
||||
}
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
#!/sbin/sh
|
||||
##########################################################################################
|
||||
#
|
||||
# Magisk Uninstaller
|
||||
# by topjohnwu
|
||||
#
|
||||
# This zip will remove all Magisk related files and revert boot image
|
||||
#
|
||||
##########################################################################################
|
||||
|
||||
INSTALLER=/tmp/uninstall
|
||||
|
||||
# Default permissions
|
||||
umask 022
|
||||
|
||||
##########################################################################################
|
||||
# Flashable update-binary preparation
|
||||
##########################################################################################
|
||||
|
||||
OUTFD=$2
|
||||
ZIP=$3
|
||||
|
||||
readlink /proc/$$/fd/$OUTFD 2>/dev/null | grep /tmp >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
OUTFD=0
|
||||
|
||||
for FD in `ls /proc/$$/fd`; do
|
||||
readlink /proc/$$/fd/$FD 2>/dev/null | grep pipe >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
ps | grep " 3 $FD " | grep -v grep >/dev/null
|
||||
if [ "$?" -eq "0" ]; then
|
||||
OUTFD=$FD
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
mkdir -p $INSTALLER
|
||||
cd $INSTALLER
|
||||
unzip -o "$ZIP"
|
||||
|
||||
##########################################################################################
|
||||
# Functions
|
||||
##########################################################################################
|
||||
|
||||
ui_print() {
|
||||
echo -n -e "ui_print $1\n" >> /proc/self/fd/$OUTFD
|
||||
echo -n -e "ui_print\n" >> /proc/self/fd/$OUTFD
|
||||
}
|
||||
|
||||
getvar() {
|
||||
local VARNAME=$1
|
||||
local VALUE=$(eval echo \$"$VARNAME");
|
||||
for FILE in /data/.magisk /cache/.magisk /system/.magisk; do
|
||||
if [ -z "$VALUE" ]; then
|
||||
LINE=$(cat $FILE 2>/dev/null | grep "$VARNAME=")
|
||||
if [ ! -z "$LINE" ]; then
|
||||
VALUE=${LINE#*=}
|
||||
fi
|
||||
fi
|
||||
done
|
||||
eval $VARNAME=\$VALUE
|
||||
}
|
||||
|
||||
is_mounted() {
|
||||
if [ ! -z "$2" ]; then
|
||||
cat /proc/mounts | grep $1 | grep $2, >/dev/null
|
||||
else
|
||||
cat /proc/mounts | grep $1 >/dev/null
|
||||
fi
|
||||
return $?
|
||||
}
|
||||
|
||||
grep_prop() {
|
||||
REGEX="s/^$1=//p"
|
||||
shift
|
||||
FILES=$@
|
||||
if [ -z "$FILES" ]; then
|
||||
FILES='/system/build.prop'
|
||||
fi
|
||||
cat $FILES 2>/dev/null | sed -n $REGEX | head -n 1
|
||||
}
|
||||
|
||||
##########################################################################################
|
||||
# Main
|
||||
##########################################################################################
|
||||
|
||||
ui_print "*****************************"
|
||||
ui_print " Magisk Uninstaller "
|
||||
ui_print "*****************************"
|
||||
|
||||
if [ ! -d "$INSTALLER/arm" ]; then
|
||||
ui_print "! Failed: Unable to extract zip file!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ui_print "- Mounting /system(ro), /cache, /data"
|
||||
mount -o ro /system 2>/dev/null
|
||||
mount /cache 2>/dev/null
|
||||
mount /data 2>/dev/null
|
||||
|
||||
if [ ! -f '/system/build.prop' ]; then
|
||||
ui_print "! Failed: /system could not be mounted!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
API=$(grep_prop ro.build.version.sdk)
|
||||
ABI=$(grep_prop ro.product.cpu.abi | cut -c-3)
|
||||
ABI2=$(grep_prop ro.product.cpu.abi2 | cut -c-3)
|
||||
ABILONG=$(grep_prop ro.product.cpu.abi)
|
||||
|
||||
ARCH=arm
|
||||
IS64BIT=false
|
||||
if [ "$ABI" = "x86" ]; then ARCH=x86; fi;
|
||||
if [ "$ABI2" = "x86" ]; then ARCH=x86; fi;
|
||||
if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=true; fi;
|
||||
if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=true; fi;
|
||||
|
||||
ui_print "- Device platform: $ARCH"
|
||||
CHROMEDIR=$INSTALLER/chromeos
|
||||
BINDIR=$INSTALLER/$ARCH
|
||||
|
||||
# Copy the binaries to /data/magisk
|
||||
mkdir -p /data/magisk 2>/dev/null
|
||||
cp -af $BINDIR/bootimgtools $CHROMEDIR /data/magisk
|
||||
|
||||
##########################################################################################
|
||||
# Detection all done, start installing
|
||||
##########################################################################################
|
||||
|
||||
ui_print "- Found Boot Image: $BOOTIMAGE"
|
||||
|
||||
if (is_mounted /data); then
|
||||
ui_print "- Running uninstaller scripts"
|
||||
sh $INSTALLER/common/magisk_uninstaller.sh
|
||||
else
|
||||
ui_print "! Data unavailable"
|
||||
ui_print "! Placing uninstall script to /cache"
|
||||
ui_print "! The device will reboot multiple times"
|
||||
cp -af $INSTALLER/common/magisk_uninstaller.sh /cache/magisk_uninstaller.sh
|
||||
umount /system
|
||||
sleep 5
|
||||
reboot
|
||||
fi
|
||||
|
||||
chmod 644 $NEWBOOT
|
||||
|
||||
umount /system
|
||||
ui_print "- Done"
|
||||
exit 0
|
||||
@@ -1 +0,0 @@
|
||||
# this is a dummy file, the magic is in update-binary
|
||||
@@ -1 +0,0 @@
|
||||
#MAGISK
|
||||
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user