Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
M
Magisk
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Magisk
Commits
cda57dd4
Commit
cda57dd4
authored
Nov 04, 2018
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fully migrate Magisk to C++
parent
4351de50
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
472 additions
and
531 deletions
+472
-531
Android.mk
native/jni/Android.mk
+12
-12
bootstages.cpp
native/jni/daemon/bootstages.cpp
+252
-269
daemon.cpp
native/jni/daemon/daemon.cpp
+3
-3
db.cpp
native/jni/daemon/db.cpp
+23
-23
log_daemon.cpp
native/jni/daemon/log_daemon.cpp
+31
-25
magisk.cpp
native/jni/daemon/magisk.cpp
+1
-2
socket.cpp
native/jni/daemon/socket.cpp
+5
-3
db.h
native/jni/include/db.h
+15
-20
img.h
native/jni/include/img.h
+8
-0
magisk.h
native/jni/include/magisk.h
+2
-2
resetprop.h
native/jni/include/resetprop.h
+6
-19
hide_utils.cpp
native/jni/magiskhide/hide_utils.cpp
+4
-4
magiskhide.cpp
native/jni/magiskhide/magiskhide.cpp
+3
-3
magiskhide.h
native/jni/magiskhide/magiskhide.h
+0
-2
applets.cpp
native/jni/misc/applets.cpp
+0
-0
img.cpp
native/jni/misc/img.cpp
+1
-1
resetprop.cpp
native/jni/resetprop/resetprop.cpp
+14
-26
connect.cpp
native/jni/su/connect.cpp
+10
-11
pts.cpp
native/jni/su/pts.cpp
+0
-1
su.cpp
native/jni/su/su.cpp
+13
-16
su.h
native/jni/su/su.h
+23
-9
su_daemon.cpp
native/jni/su/su_daemon.cpp
+34
-31
Android.mk
native/jni/utils/Android.mk
+1
-2
array.h
native/jni/utils/include/array.h
+11
-1
selinux.h
native/jni/utils/include/selinux.h
+0
-1
utils.h
native/jni/utils/include/utils.h
+0
-4
legacy.c
native/jni/utils/legacy.c
+0
-41
No files found.
native/jni/Android.mk
View file @
cda57dd4
...
...
@@ -32,14 +32,14 @@ LOCAL_C_INCLUDES := \
$(LIBUTILS)
LOCAL_SRC_FILES := \
misc/applets.c \
misc/img.c \
daemon/magisk.c \
daemon/daemon.c \
daemon/log_daemon.c \
daemon/bootstages.c \
daemon/socket.c \
daemon/db.c \
misc/applets.c
pp
\
misc/img.c
pp
\
daemon/magisk.c
pp
\
daemon/daemon.c
pp
\
daemon/log_daemon.c
pp
\
daemon/bootstages.c
pp
\
daemon/socket.c
pp
\
daemon/db.c
pp
\
magiskhide/magiskhide.cpp \
magiskhide/proc_monitor.cpp \
magiskhide/hide_utils.cpp \
...
...
@@ -47,10 +47,10 @@ LOCAL_SRC_FILES := \
resetprop/resetprop.cpp \
resetprop/system_property_api.cpp \
resetprop/system_property_set.cpp \
su/su.c \
su/connect.c \
su/pts.c \
su/su_daemon.c
su/su.c
pp
\
su/connect.c
pp
\
su/pts.c
pp
\
su/su_daemon.c
pp
LOCAL_LDLIBS := -llog
include $(BUILD_EXECUTABLE)
...
...
native/jni/daemon/bootstages.c
→
native/jni/daemon/bootstages.c
pp
View file @
cda57dd4
...
...
@@ -24,13 +24,13 @@
#include "flags.h"
static
char
buf
[
PATH_MAX
],
buf2
[
PATH_MAX
];
static
struct
vector
module_list
;
static
Array
<
char
*>
module_list
;
extern
char
**
environ
;
static
int
bind_mount
(
const
char
*
from
,
const
char
*
to
)
;
/***************
***
*
Node structure
*
***************
***
/
/***************
*
Magic Mount
*
***************/
// Precedence: MODULE > SKEL > INTER > DUMMY
#define IS_DUMMY 0x01
/* mount from mirror */
...
...
@@ -42,157 +42,83 @@ extern char **environ;
#define IS_LNK(n) (n->type == DT_LNK)
#define IS_REG(n) (n->type == DT_REG)
struct
node_entry
{
class
node_entry
{
public
:
const
char
*
module
;
/* Only used when status & IS_MODULE */
char
*
name
;
uint8_t
type
;
uint8_t
status
;
struct
node_entry
*
parent
;
struct
vector
*
children
;
node_entry
*
parent
;
Array
<
node_entry
*>
children
;
node_entry
()
=
default
;
node_entry
(
const
char
*
,
const
char
*
,
uint8_t
type
=
0
,
uint8_t
status
=
0
);
node_entry
(
const
char
*
,
uint8_t
type
=
0
,
uint8_t
status
=
0
);
~
node_entry
();
void
create_module_tree
(
const
char
*
module
);
void
magic_mount
();
private
:
char
*
get_path
();
node_entry
*
insert
(
node_entry
*
);
void
clone_skeleton
();
int
get_path
(
char
*
path
);
};
static
void
concat_path
(
struct
node_entry
*
node
)
{
if
(
node
->
parent
)
concat_path
(
node
->
parent
);
size_t
len
=
strlen
(
buf
);
buf
[
len
]
=
'/'
;
strcpy
(
buf
+
len
+
1
,
node
->
name
);
node_entry
::
node_entry
(
const
char
*
module
,
const
char
*
name
,
uint8_t
type
,
uint8_t
status
)
:
node_entry
(
name
,
type
,
status
)
{
this
->
module
=
module
;
}
static
char
*
get_full_path
(
struct
node_entry
*
node
)
{
buf
[
0
]
=
'\0'
;
concat_path
(
node
);
return
strdup
(
buf
);
node_entry
::
node_entry
(
const
char
*
name
,
uint8_t
type
,
uint8_t
status
)
:
type
(
type
),
status
(
status
),
parent
(
nullptr
)
{
this
->
name
=
strdup
(
name
);
}
// Free the node
static
void
destroy_node
(
struct
node_entry
*
node
)
{
free
(
node
->
name
);
vec_destroy
(
node
->
children
);
free
(
node
->
children
);
free
(
node
);
node_entry
::~
node_entry
()
{
free
(
name
);
for
(
auto
&
node
:
children
)
delete
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_cur
(
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
;
}
/***********
* setenvs *
***********/
static
void
set_path
()
{
char
buffer
[
512
];
sprintf
(
buffer
,
BBPATH
":%s"
,
getenv
(
"PATH"
));
setenv
(
"PATH"
,
buffer
,
1
);
char
*
node_entry
::
get_path
()
{
get_path
(
buf
);
return
strdup
(
buf
);
}
static
void
set_mirror_path
()
{
setenv
(
"PATH"
,
BBPATH
":/sbin:"
MIRRDIR
"/system/bin:"
MIRRDIR
"/system/xbin:"
MIRRDIR
"/vendor/bin"
,
1
);
int
node_entry
::
get_path
(
char
*
path
)
{
int
len
=
0
;
if
(
parent
)
len
=
parent
->
get_path
(
path
);
len
+=
sprintf
(
path
+
len
,
"/%s"
,
name
);
return
len
;
}
/***********
* Scripts *
***********/
static
void
exec_common_script
(
const
char
*
stage
)
{
DIR
*
dir
;
struct
dirent
*
entry
;
snprintf
(
buf2
,
PATH_MAX
,
"%s/%s.d"
,
COREDIR
,
stage
);
if
(
!
(
dir
=
xopendir
(
buf2
)))
return
;
while
((
entry
=
xreaddir
(
dir
)))
{
if
(
entry
->
d_type
==
DT_REG
)
{
snprintf
(
buf2
,
PATH_MAX
,
"%s/%s.d/%s"
,
COREDIR
,
stage
,
entry
->
d_name
);
if
(
access
(
buf2
,
X_OK
)
==
-
1
)
continue
;
LOGI
(
"%s.d: exec [%s]
\n
"
,
stage
,
entry
->
d_name
);
int
pid
=
exec_command
(
0
,
NULL
,
strcmp
(
stage
,
"post-fs-data"
)
?
set_path
:
set_mirror_path
,
"sh"
,
buf2
,
NULL
);
if
(
pid
!=
-
1
)
waitpid
(
pid
,
NULL
,
0
);
node_entry
*
node_entry
::
insert
(
node_entry
*
node
)
{
node
->
parent
=
this
;
for
(
auto
&
child
:
children
)
{
if
(
strcmp
(
child
->
name
,
node
->
name
)
==
0
)
{
if
(
node
->
status
>
child
->
status
)
{
// The new node has higher precedence
delete
child
;
child
=
node
;
return
node
;
}
else
{
delete
node
;
return
child
;
}
}
}
closedir
(
dir
)
;
children
.
push_back
(
node
);
return
node
;
}
static
void
exec_module_script
(
const
char
*
stage
)
{
char
*
module
;
vec_for_each
(
&
module_list
,
module
)
{
snprintf
(
buf2
,
PATH_MAX
,
"%s/%s/%s.sh"
,
MOUNTPOINT
,
module
,
stage
);
snprintf
(
buf
,
PATH_MAX
,
"%s/%s/disable"
,
MOUNTPOINT
,
module
);
if
(
access
(
buf2
,
F_OK
)
==
-
1
||
access
(
buf
,
F_OK
)
==
0
)
continue
;
LOGI
(
"%s: exec [%s.sh]
\n
"
,
module
,
stage
);
int
pid
=
exec_command
(
0
,
NULL
,
strcmp
(
stage
,
"post-fs-data"
)
?
set_path
:
set_mirror_path
,
"sh"
,
buf2
,
NULL
);
if
(
pid
!=
-
1
)
waitpid
(
pid
,
NULL
,
0
);
}
}
/***************
* Magic Mount *
***************/
static
int
bind_mount
(
const
char
*
from
,
const
char
*
to
)
{
int
ret
=
xmount
(
from
,
to
,
NULL
,
MS_BIND
,
NULL
);
#ifdef MAGISK_DEBUG
LOGI
(
"bind_mount: %s <- %s
\n
"
,
to
,
from
);
#else
LOGI
(
"bind_mount: %s
\n
"
,
to
);
#endif
return
ret
;
}
static
void
construct_tree
(
const
char
*
module
,
struct
node_entry
*
parent
)
{
void
node_entry
::
create_module_tree
(
const
char
*
module
)
{
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
);
char
*
full_path
=
get_path
(
);
snprintf
(
buf
,
PATH_MAX
,
"%s/%s%s"
,
MOUNTPOINT
,
module
,
full
_path
);
if
(
!
(
dir
=
xopendir
(
buf
)))
goto
cleanup
;
...
...
@@ -201,31 +127,28 @@ static void construct_tree(const char *module, struct node_entry *parent) {
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
);
node_entry
*
node
=
new
node_entry
(
module
,
entry
->
d_name
,
entry
->
d_type
);
snprintf
(
buf
,
PATH_MAX
,
"%s/%s"
,
full_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
* 3. Target file is a symlink
(exclude /system/vendor)
*/
int
clone
=
0
;
bool
clone
=
false
;
if
(
IS_LNK
(
node
)
||
access
(
buf
,
F_OK
)
==
-
1
)
{
clone
=
1
;
}
else
if
(
parent
->
parent
!=
NULL
||
strcmp
(
node
->
name
,
"vendor"
)
!=
0
)
{
clone
=
true
;
}
else
if
(
parent
!=
nullptr
||
strcmp
(
node
->
name
,
"vendor"
)
!=
0
)
{
struct
stat
s
;
xstat
(
buf
,
&
s
);
if
(
S_ISLNK
(
s
.
st_mode
))
clone
=
1
;
clone
=
true
;
}
if
(
clone
)
{
// Mark
the parent folder
as a skeleton
parent
->
status
|=
IS_SKEL
;
/* This will not overwrite if parent is module */
// Mark
self
as a skeleton
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
...
...
@@ -238,29 +161,28 @@ static void construct_tree(const char *module, struct node_entry *parent) {
node
->
status
=
IS_INTER
;
}
}
else
if
(
IS_REG
(
node
))
{
// This is a
leaf, mark as target
// This is a
file, mark as leaf
node
->
status
=
IS_MODULE
;
}
node
=
insert
_child
(
parent
,
node
);
node
=
insert
(
node
);
if
(
node
->
status
&
(
IS_SKEL
|
IS_INTER
))
{
// Intermediate folder, travel deeper
construct_tree
(
module
,
nod
e
);
node
->
create_module_tree
(
modul
e
);
}
}
closedir
(
dir
);
cleanup
:
free
(
parent
_path
);
free
(
full
_path
);
}
static
void
clone_skeleton
(
struct
node_entry
*
node
)
{
void
node_entry
::
clone_skeleton
(
)
{
DIR
*
dir
;
struct
dirent
*
entry
;
struct
node_entry
*
dummy
,
*
child
;
struct
node_entry
*
dummy
;
// Clone the structure
char
*
full_path
=
get_
full_path
(
node
);
char
*
full_path
=
get_
path
(
);
snprintf
(
buf
,
PATH_MAX
,
"%s%s"
,
MIRRDIR
,
full_path
);
if
(
!
(
dir
=
xopendir
(
buf
)))
goto
cleanup
;
...
...
@@ -268,28 +190,25 @@ static void clone_skeleton(struct node_entry *node) {
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
);
dummy
=
new
node_entry
(
entry
->
d_name
,
entry
->
d_type
,
IS_DUMMY
);
insert
(
dummy
);
}
closedir
(
dir
);
if
(
node
->
status
&
IS_SKEL
)
{
if
(
status
&
IS_SKEL
)
{
struct
stat
s
;
char
*
con
;
xstat
(
full_path
,
&
s
);
getfilecon
(
full_path
,
&
con
);
LOGI
(
"mnt_tmpfs : %s
\n
"
,
full_path
);
xmount
(
"tmpfs"
,
full_path
,
"tmpfs"
,
0
,
NULL
);
xmount
(
"tmpfs"
,
full_path
,
"tmpfs"
,
0
,
nullptr
);
chmod
(
full_path
,
s
.
st_mode
&
0777
);
chown
(
full_path
,
s
.
st_uid
,
s
.
st_gid
);
setfilecon
(
full_path
,
con
);
free
(
con
);
}
vec_for_each
(
node
->
children
,
child
)
{
for
(
auto
&
child
:
children
)
{
snprintf
(
buf
,
PATH_MAX
,
"%s/%s"
,
full_path
,
child
->
name
);
// Create the dummy file/directory
...
...
@@ -299,7 +218,7 @@ static void clone_skeleton(struct node_entry *node) {
close
(
creat
(
buf
,
0644
));
// Links will be handled later
if
(
child
->
parent
->
parent
==
NULL
&&
strcmp
(
child
->
name
,
"vendor"
)
==
0
)
{
if
(
child
->
parent
->
parent
==
nullptr
&&
strcmp
(
child
->
name
,
"vendor"
)
==
0
)
{
if
(
IS_LNK
(
child
))
{
cp_afc
(
MIRRDIR
"/system/vendor"
,
"/system/vendor"
);
LOGI
(
"creat_link: %s <- %s
\n
"
,
"/system/vendor"
,
MIRRDIR
"/system/vendor"
);
...
...
@@ -311,7 +230,7 @@ static void clone_skeleton(struct node_entry *node) {
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 an intermediate folder, recursive clone
c
lone_skeleton
(
child
);
c
hild
->
clone_skeleton
(
);
continue
;
}
else
if
(
child
->
status
&
IS_DUMMY
)
{
// Mount from mirror to dummy file
...
...
@@ -336,28 +255,86 @@ 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
);
void
node_entry
::
magic_mount
()
{
if
(
status
&
IS_MODULE
)
{
// Mount module item
char
*
real_path
=
get_path
();
snprintf
(
buf
,
PATH_MAX
,
"%s/%s%s"
,
MOUNTPOINT
,
module
,
real_path
);
bind_mount
(
buf
,
real_path
);
free
(
real_path
);
}
else
if
(
node
->
status
&
IS_SKEL
)
{
}
else
if
(
status
&
IS_SKEL
)
{
// The node is labeled to be cloned with skeleton, lets do it
clone_skeleton
(
node
);
}
else
if
(
node
->
status
&
IS_INTER
)
{
clone_skeleton
();
}
else
if
(
status
&
IS_INTER
)
{
// It's an intermediate node, travel deeper
vec_for_each
(
node
->
children
,
child
)
magic_mount
(
child
);
for
(
auto
&
child
:
children
)
child
->
magic_mount
(
);
}
// The only thing goes here should be vendor placeholder
// There should be no dummies, so don't need to handle it here
}
/***********
* setenvs *
***********/
static
void
set_path
()
{
char
buffer
[
512
];
sprintf
(
buffer
,
BBPATH
":%s"
,
getenv
(
"PATH"
));
setenv
(
"PATH"
,
buffer
,
1
);
}
static
void
set_mirror_path
()
{
setenv
(
"PATH"
,
BBPATH
":/sbin:"
MIRRDIR
"/system/bin:"
MIRRDIR
"/system/xbin:"
MIRRDIR
"/vendor/bin"
,
1
);
}
/***********
* Scripts *
***********/
static
void
exec_common_script
(
const
char
*
stage
)
{
DIR
*
dir
;
struct
dirent
*
entry
;
snprintf
(
buf2
,
PATH_MAX
,
"%s/%s.d"
,
COREDIR
,
stage
);
if
(
!
(
dir
=
xopendir
(
buf2
)))
return
;
while
((
entry
=
xreaddir
(
dir
)))
{
if
(
entry
->
d_type
==
DT_REG
)
{
snprintf
(
buf2
,
PATH_MAX
,
"%s/%s.d/%s"
,
COREDIR
,
stage
,
entry
->
d_name
);
if
(
access
(
buf2
,
X_OK
)
==
-
1
)
continue
;
LOGI
(
"%s.d: exec [%s]
\n
"
,
stage
,
entry
->
d_name
);
int
pid
=
exec_command
(
0
,
nullptr
,
strcmp
(
stage
,
"post-fs-data"
)
?
set_path
:
set_mirror_path
,
"sh"
,
buf2
,
nullptr
);
if
(
pid
!=
-
1
)
waitpid
(
pid
,
nullptr
,
0
);
}
}
closedir
(
dir
);
}
static
void
exec_module_script
(
const
char
*
stage
)
{
for
(
auto
&
module
:
module_list
)
{
snprintf
(
buf2
,
PATH_MAX
,
"%s/%s/%s.sh"
,
MOUNTPOINT
,
module
,
stage
);
snprintf
(
buf
,
PATH_MAX
,
"%s/%s/disable"
,
MOUNTPOINT
,
module
);
if
(
access
(
buf2
,
F_OK
)
==
-
1
||
access
(
buf
,
F_OK
)
==
0
)
continue
;
LOGI
(
"%s: exec [%s.sh]
\n
"
,
module
,
stage
);
int
pid
=
exec_command
(
0
,
nullptr
,
strcmp
(
stage
,
"post-fs-data"
)
?
set_path
:
set_mirror_path
,
"sh"
,
buf2
,
nullptr
);
if
(
pid
!=
-
1
)
waitpid
(
pid
,
nullptr
,
0
);
}
}
/****************
* Simple Mount *
****************/
...
...
@@ -399,8 +376,18 @@ static void simple_mount(const char *path) {
* Miscellaneous *
*****************/
#define alt_img ((char *[]) \
{ "/cache/magisk.img", "/data/magisk_merge.img", "/data/adb/magisk_merge.img", NULL })
static
int
bind_mount
(
const
char
*
from
,
const
char
*
to
)
{
int
ret
=
xmount
(
from
,
to
,
nullptr
,
MS_BIND
,
nullptr
);
#ifdef MAGISK_DEBUG
LOGI
(
"bind_mount: %s <- %s
\n
"
,
to
,
from
);
#else
LOGI
(
"bind_mount: %s
\n
"
,
to
);
#endif
return
ret
;
}
#define alt_img ((const char *[]) \
{ "/cache/magisk.img", "/data/magisk_merge.img", "/data/adb/magisk_merge.img", nullptr })
static
int
prepare_img
()
{
// Merge images
...
...
@@ -419,7 +406,7 @@ static int prepare_img() {
LOGI
(
"* Mounting "
MAINIMG
"
\n
"
);
// Mounting magisk image
char
*
magiskloop
=
mount_image
(
MAINIMG
,
MOUNTPOINT
);
if
(
magiskloop
==
NULL
)
if
(
magiskloop
==
nullptr
)
return
1
;
xmkdir
(
COREDIR
,
0755
);
...
...
@@ -447,7 +434,7 @@ static int prepare_img() {
snprintf
(
buf
,
PATH_MAX
,
"%s/%s/disable"
,
MOUNTPOINT
,
entry
->
d_name
);
if
(
access
(
buf
,
F_OK
)
==
0
)
continue
;
vec_push_back
(
&
module_list
,
strdup
(
entry
->
d_name
));
module_list
.
push_back
(
strdup
(
entry
->
d_name
));
}
}
closedir
(
dir
);
...
...
@@ -459,19 +446,19 @@ static int prepare_img() {
}
static
void
install_apk
(
const
char
*
apk
)
{
setfilecon
(
apk
,
"u:object_r:"
SEPOL_FILE_DOMAIN
":s0"
);
setfilecon
(
apk
,
"u:object_r:"
SEPOL_FILE_DOMAIN
":s0"
);
while
(
1
)
{
sleep
(
5
);
LOGD
(
"apk_install: attempting to install APK"
);
int
apk_res
=
-
1
,
pid
;
pid
=
exec_command
(
1
,
&
apk_res
,
NULL
,
"/system/bin/pm"
,
"install"
,
"-r"
,
apk
,
NULL
);
pid
=
exec_command
(
1
,
&
apk_res
,
nullptr
,
"/system/bin/pm"
,
"install"
,
"-r"
,
apk
,
nullptr
);
if
(
pid
!=
-
1
)
{
int
err
=
0
;
while
(
fdgets
(
buf
,
PATH_MAX
,
apk_res
)
>
0
)
{
LOGD
(
"apk_install: %s"
,
buf
);
err
|=
strstr
(
buf
,
"Error:"
)
!=
NULL
;
err
|=
strstr
(
buf
,
"Error:"
)
!=
nullptr
;
}
waitpid
(
pid
,
NULL
,
0
);
waitpid
(
pid
,
nullptr
,
0
);
close
(
apk_res
);
// Keep trying until pm is started
if
(
err
)
...
...
@@ -482,38 +469,35 @@ static void install_apk(const char *apk) {
unlink
(
apk
);
}
static
int
check_data
()
{
struct
vector
v
;
vec_init
(
&
v
);
file_to_vector
(
"/proc/mounts"
,
&
v
);
char
*
line
;
int
mnt
=
0
;
vec_for_each
(
&
v
,
line
)
{
if
(
strstr
(
line
,
" /data "
)
&&
strstr
(
line
,
"tmpfs"
)
==
NULL
)
{
mnt
=
1
;
break
;
}
static
bool
check_data
()
{
bool
mnt
=
false
;
bool
data
=
false
;
Array
<
char
*>
mounts
;
file_to_array
(
"/proc/mounts"
,
mounts
);
for
(
auto
&
line
:
mounts
)
{
if
(
strstr
(
line
,
" /data "
)
&&
strstr
(
line
,
"tmpfs"
)
==
nullptr
)
mnt
=
true
;
free
(
line
);
}
vec_deep_destroy
(
&
v
);
int
data
=
0
;
mounts
.
clear
();
if
(
mnt
)
{
char
*
crypto
=
getprop
(
"ro.crypto.state"
);
if
(
crypto
!=
NULL
)
{
if
(
crypto
!=
nullptr
)
{
if
(
strcmp
(
crypto
,
"unencrypted"
)
==
0
)
{
// Unencrypted, we can directly access data
data
=
1
;
data
=
true
;
}
else
{
// Encrypted, check whether vold is started
char
*
vold
=
getprop
(
"init.svc.vold"
);
if
(
vold
!=
NULL
)
{
if
(
vold
!=
nullptr
)
{
free
(
vold
);
data
=
1
;
data
=
true
;
}
}
free
(
crypto
);
}
else
{
// ro.crypto.state is not set, assume it's unencrypted
data
=
1
;
data
=
true
;
}
}
return
data
;
...
...
@@ -521,18 +505,18 @@ static int check_data() {
extern
int
launch_magiskhide
();
static
void
*
start_magisk_hide
(
void
*
args
)
{
static
void
*
start_magisk_hide
(
void
*
)
{
launch_magiskhide
();
return
NULL
;
return
nullptr
;
}
static
void
auto_start_magiskhide
()
{
if
(
!
start_log_daemon
())
return
;
char
*
hide_prop
=
getprop
2
(
MAGISKHIDE_PROP
,
1
);
if
(
hide_prop
==
NULL
||
strcmp
(
hide_prop
,
"0"
)
!=
0
)
{
char
*
hide_prop
=
getprop
(
MAGISKHIDE_PROP
,
1
);
if
(
hide_prop
==
nullptr
||
strcmp
(
hide_prop
,
"0"
)
!=
0
)
{
pthread_t
thread
;
xpthread_create
(
&
thread
,
NULL
,
start_magisk_hide
,
NULL
);
xpthread_create
(
&
thread
,
nullptr
,
start_magisk_hide
,
nullptr
);
pthread_detach
(
thread
);
}
free
(
hide_prop
);
...
...
@@ -565,7 +549,7 @@ void unlock_blocks() {
static
void
unblock_boot_process
()
{
close
(
xopen
(
UNBLOCKFILE
,
O_RDONLY
|
O_CREAT
,
0
));
pthread_exit
(
NULL
);
pthread_exit
(
nullptr
);
}
static
const
char
wrapper
[]
=
...
...
@@ -621,7 +605,7 @@ void startup() {
void
*
magisk
,
*
init
;
size_t
magisk_size
,
init_size
;
xmount
(
NULL
,
"/"
,
NULL
,
MS_REMOUNT
,
NULL
);
xmount
(
nullptr
,
"/"
,
nullptr
,
MS_REMOUNT
,
nullptr
);
// Remove some traits of Magisk
unlink
(
MAGISKRC
);
...
...
@@ -645,7 +629,7 @@ void startup() {
close
(
sbin
);
// Mount the /sbin tmpfs overlay
xmount
(
"tmpfs"
,
"/sbin"
,
"tmpfs"
,
0
,
NULL
);
xmount
(
"tmpfs"
,
"/sbin"
,
"tmpfs"
,
0
,
nullptr
);
chmod
(
"/sbin"
,
0755
);
setfilecon
(
"/sbin"
,
"u:object_r:rootfs:s0"
);
sbin
=
xopen
(
"/sbin"
,
O_RDONLY
|
O_CLOEXEC
);
...
...
@@ -665,15 +649,15 @@ void startup() {
fd
=
creat
(
"/sbin/magisk"
,
0755
);
xwrite
(
fd
,
wrapper
,
sizeof
(
wrapper
)
-
1
);
close
(
fd
);
setfilecon
(
"/sbin/magisk.bin"
,
"u:object_r:"
SEPOL_FILE_DOMAIN
":s0"
);
setfilecon
(
"/sbin/magisk"
,
"u:object_r:"
SEPOL_FILE_DOMAIN
":s0"
);
setfilecon
(
"/sbin/magisk.bin"
,
"u:object_r:"
SEPOL_FILE_DOMAIN
":s0"
);
setfilecon
(
"/sbin/magisk"
,
"u:object_r:"
SEPOL_FILE_DOMAIN
":s0"
);
// Setup magiskinit symlinks
fd
=
creat
(
"/sbin/magiskinit"
,
0755
);
xwrite
(
fd
,
init
,
init_size
);
close
(
fd
);
free
(
init
);
setfilecon
(
"/sbin/magiskinit"
,
"u:object_r:"
SEPOL_FILE_DOMAIN
":s0"
);
setfilecon
(
"/sbin/magiskinit"
,
"u:object_r:"
SEPOL_FILE_DOMAIN
":s0"
);
for
(
int
i
=
0
;
init_applet
[
i
];
++
i
)
{
snprintf
(
buf
,
PATH_MAX
,
"/sbin/%s"
,
init_applet
[
i
]);
xsymlink
(
"/sbin/magiskinit"
,
buf
);
...
...
@@ -691,10 +675,10 @@ void startup() {
close
(
root
);
// Alternative binaries paths
char
*
alt_bin
[]
=
{
"/cache/data_bin"
,
"/data/magisk"
,
c
onst
c
har
*
alt_bin
[]
=
{
"/cache/data_bin"
,
"/data/magisk"
,
"/data/data/com.topjohnwu.magisk/install"
,
"/data/user_de/0/com.topjohnwu.magisk/install"
,
NULL
};
c
har
*
bin_path
=
NULL
;
"/data/user_de/0/com.topjohnwu.magisk/install"
,
nullptr
};
c
onst
char
*
bin_path
=
nullptr
;
for
(
int
i
=
0
;
alt_bin
[
i
];
++
i
)
{
struct
stat
st
;
if
(
lstat
(
alt_bin
[
i
],
&
st
)
!=
-
1
&&
!
S_ISLNK
(
st
.
st_mode
))
{
...
...
@@ -723,19 +707,17 @@ void startup() {
xmkdir
(
BLOCKDIR
,
0755
);
LOGI
(
"* Mounting mirrors"
);
struct
vector
mounts
;
vec_init
(
&
mounts
);
file_to_vector
(
"/proc/mounts"
,
&
mounts
);
char
*
line
;
Array
<
char
*>
mounts
;
file_to_array
(
"/proc/mounts"
,
mounts
);
int
skip_initramfs
=
0
;
// Check whether skip_initramfs device
vec_for_each
(
&
mounts
,
line
)
{
for
(
auto
&
line
:
mounts
)
{
if
(
strstr
(
line
,
" /system_root "
))
{
bind_mount
(
"/system_root/system"
,
MIRRDIR
"/system"
);
skip_initramfs
=
1
;
}
else
if
(
!
skip_initramfs
&&
strstr
(
line
,
" /system "
))
{
sscanf
(
line
,
"%s %*s %s"
,
buf
,
buf2
);
xmount
(
buf
,
MIRRDIR
"/system"
,
buf2
,
MS_RDONLY
,
NULL
);
xmount
(
buf
,
MIRRDIR
"/system"
,
buf2
,
MS_RDONLY
,
nullptr
);
#ifdef MAGISK_DEBUG
LOGI
(
"mount: %s <- %s
\n
"
,
MIRRDIR
"/system"
,
buf
);
#else
...
...
@@ -745,7 +727,7 @@ void startup() {
seperate_vendor
=
1
;
sscanf
(
line
,
"%s %*s %s"
,
buf
,
buf2
);
xmkdir
(
MIRRDIR
"/vendor"
,
0755
);
xmount
(
buf
,
MIRRDIR
"/vendor"
,
buf2
,
MS_RDONLY
,
NULL
);
xmount
(
buf
,
MIRRDIR
"/vendor"
,
buf2
,
MS_RDONLY
,
nullptr
);
#ifdef MAGISK_DEBUG
LOGI
(
"mount: %s <- %s
\n
"
,
MIRRDIR
"/vendor"
,
buf
);
#else
...
...
@@ -754,7 +736,7 @@ void startup() {
}
free
(
line
);
}
vec_destroy
(
&
mounts
);
mounts
.
clear
(
);
if
(
!
seperate_vendor
)
{
xsymlink
(
MIRRDIR
"/system/vendor"
,
MIRRDIR
"/vendor"
);
#ifdef MAGISK_DEBUG
...
...
@@ -767,12 +749,23 @@ void startup() {
bind_mount
(
DATABIN
,
MIRRDIR
"/bin"
);
if
(
access
(
MIRRDIR
"/bin/busybox"
,
X_OK
)
==
0
)
{
LOGI
(
"* Setting up internal busybox"
);
exec_command_sync
(
MIRRDIR
"/bin/busybox"
,
"--install"
,
"-s"
,
BBPATH
,
NULL
);
exec_command_sync
(
MIRRDIR
"/bin/busybox"
,
"--install"
,
"-s"
,
BBPATH
,
nullptr
);
xsymlink
(
MIRRDIR
"/bin/busybox"
,
BBPATH
"/busybox"
);
}
// Start post-fs-data mode
execl
(
"/sbin/magisk.bin"
,
"magisk"
,
"--post-fs-data"
,
NULL
);
execl
(
"/sbin/magisk.bin"
,
"magisk"
,
"--post-fs-data"
,
nullptr
);
}
static
void
core_only
()
{
// Systemless hosts
if
(
access
(
HOSTSFILE
,
F_OK
)
==
0
)
{
LOGI
(
"* Enabling systemless hosts file support"
);
bind_mount
(
HOSTSFILE
,
"/system/etc/hosts"
);
}
auto_start_magiskhide
();
unblock_boot_process
();
}
void
post_fs_data
(
int
client
)
{
...
...
@@ -783,20 +776,20 @@ void post_fs_data(int client) {
// If post-fs-data mode is started, it means startup succeeded
setup_done
=
1
;
xmount
(
NULL
,
"/"
,
NULL
,
MS_REMOUNT
|
MS_RDONLY
,
NULL
);
xmount
(
nullptr
,
"/"
,
nullptr
,
MS_REMOUNT
|
MS_RDONLY
,
nullptr
);
// Start log_daemon
start_log_daemon
();
LOGI
(
"** post-fs-data mode running
\n
"
);
// Allocate buffer
vec_init
(
&
module_list
);
// Merge, trim, mount magisk.img, which will also travel through the modules
// After this, it will create the module list
if
(
prepare_img
())
goto
core_only
;
// Mounting fails, we can only do core only stuffs
if
(
prepare_img
())
{
// Mounting fails, we can only do core only stuffs
core_only
();
return
;
}
restorecon
();
chmod
(
SECURE_DIR
,
0700
);
...
...
@@ -806,30 +799,31 @@ void post_fs_data(int client) {
exec_common_script
(
"post-fs-data"
);
// Core only mode
if
(
access
(
DISABLEFILE
,
F_OK
)
==
0
)
goto
core_only
;
if
(
access
(
DISABLEFILE
,
F_OK
)
==
0
)
{
core_only
();
return
;
}
// Execute module scripts
LOGI
(
"* Running module post-fs-data scripts
\n
"
);
exec_module_script
(
"post-fs-data"
);
char
*
module
;
struct
node_entry
*
sys_root
,
*
ven_root
=
NULL
,
*
child
;
// Create the system root entry
sys_root
=
xcalloc
(
sizeof
(
*
sys_root
),
1
);
sys_root
->
name
=
strdup
(
"system"
);
node_entry
*
sys_root
=
new
node_entry
(
"system"
);
sys_root
->
status
=
IS_INTER
;
int
has_modules
=
0
;
// Vendor root entry
node_entry
*
ven_root
=
nullptr
;
bool
has_modules
=
false
;
LOGI
(
"* Loading modules
\n
"
);
vec_for_each
(
&
module_list
,
module
)
{
for
(
auto
&
module
:
module_list
)
{
// Read props
snprintf
(
buf
,
PATH_MAX
,
"%s/%s/system.prop"
,
MOUNTPOINT
,
module
);
if
(
access
(
buf
,
F_OK
)
==
0
)
{
LOGI
(
"%s: loading [system.prop]
\n
"
,
module
);
re
ad_prop_file
(
buf
,
0
);
lo
ad_prop_file
(
buf
,
0
);
}
// Check whether enable auto_mount
snprintf
(
buf
,
PATH_MAX
,
"%s/%s/auto_mount"
,
MOUNTPOINT
,
module
);
...
...
@@ -841,7 +835,7 @@ void post_fs_data(int client) {
continue
;
// Construct structure
has_modules
=
1
;
has_modules
=
true
;
LOGI
(
"%s: constructing magic mount structure
\n
"
,
module
);
// If /system/vendor exists in module, create a link outside
snprintf
(
buf
,
PATH_MAX
,
"%s/%s/system/vendor"
,
MOUNTPOINT
,
module
);
...
...
@@ -850,44 +844,31 @@ void post_fs_data(int client) {
unlink
(
buf2
);
xsymlink
(
buf
,
buf2
);
}
construct_tree
(
module
,
sys_root
);
sys_root
->
create_module_tree
(
module
);
}
if
(
has_modules
)
{
// Extract the vendor node out of system tree and swap with placeholder
vec_for_each
(
sys_root
->
children
,
child
)
{
for
(
auto
&
child
:
sys_root
->
children
)
{
if
(
strcmp
(
child
->
name
,
"vendor"
)
==
0
)
{
ven_root
=
child
;
child
=
xcalloc
(
sizeof
(
*
child
),
1
);
child
->
type
=
seperate_vendor
?
DT_LNK
:
DT_DIR
;
child
=
new
node_entry
(
"vendor"
,
seperate_vendor
?
DT_LNK
:
DT_DIR
);
child
->
parent
=
ven_root
->
parent
;
child
->
name
=
strdup
(
"vendor"
);
child
->
status
=
0
;
// Swap!
vec_cur
(
sys_root
->
children
)
=
child
;
ven_root
->
parent
=
NULL
;
ven_root
->
parent
=
nullptr
;
break
;
}
}
// Magic!!
magic_mount
(
sys_root
);
if
(
ven_root
)
magic_mount
(
ven_root
);
sys_root
->
magic_mount
(
);
if
(
ven_root
)
ven_root
->
magic_mount
(
);
}
// Cleanup memory
destroy_subtree
(
sys_root
);
if
(
ven_root
)
destroy_subtree
(
ven_root
);
core_only:
// Systemless hosts
if
(
access
(
HOSTSFILE
,
F_OK
)
==
0
)
{
LOGI
(
"* Enabling systemless hosts file support"
);
bind_mount
(
HOSTSFILE
,
"/system/etc/hosts"
);
}
delete
sys_root
;
if
(
ven_root
)
delete
ven_root
;
auto_start_magiskhide
();
unblock_boot_process
();
core_only
();
}
void
late_start
(
int
client
)
{
...
...
@@ -903,7 +884,7 @@ void late_start(int client) {
if
(
!
setup_done
)
{
// The setup failed for some reason, reboot and try again
exec_command_sync
(
"/system/bin/reboot"
,
NULL
);
exec_command_sync
(
"/system/bin/reboot"
,
nullptr
);
return
;
}
...
...
@@ -932,9 +913,9 @@ core_only:
struct
db_strings
str
;
memset
(
&
str
,
0
,
sizeof
(
str
));
get_db_strings
(
db
,
SU_MANAGER
,
&
str
);
if
(
validate_manager
(
str
.
s
[
SU_MANAGER
],
0
,
NULL
))
{
if
(
validate_manager
(
str
.
s
[
SU_MANAGER
],
0
,
nullptr
))
{
// There is no manager installed, install the stub
exec_command_sync
(
"/sbin/magiskinit"
,
"-x"
,
"manager"
,
"/data/magisk.apk"
,
NULL
);
exec_command_sync
(
"/sbin/magiskinit"
,
"-x"
,
"manager"
,
"/data/magisk.apk"
,
nullptr
);
install_apk
(
"/data/magisk.apk"
);
}
sqlite3_close_v2
(
db
);
...
...
@@ -942,7 +923,9 @@ core_only:
}
// All boot stage done, cleanup
vec_deep_destroy
(
&
module_list
);
for
(
auto
&
module
:
module_list
)
free
(
module
);
module_list
.
clear
();
}
void
boot_complete
(
int
client
)
{
...
...
native/jni/daemon/daemon.c
→
native/jni/daemon/daemon.c
pp
View file @
cda57dd4
...
...
@@ -31,7 +31,7 @@ static void get_client_cred(int fd, struct ucred *cred) {
static
void
*
request_handler
(
void
*
args
)
{
int
client
=
*
((
int
*
)
args
);
free
(
args
)
;
delete
(
int
*
)
args
;
int
req
=
read_int
(
client
);
struct
ucred
credential
;
...
...
@@ -88,7 +88,7 @@ static void *request_handler(void *args) {
static
void
main_daemon
()
{
android_logging
();
setsid
();
setcon
(
"u:r:"
SEPOL_PROC_DOMAIN
":s0"
);
setcon
(
"u:r:"
SEPOL_PROC_DOMAIN
":s0"
);
int
fd
=
xopen
(
"/dev/null"
,
O_RDWR
|
O_CLOEXEC
);
xdup2
(
fd
,
STDOUT_FILENO
);
xdup2
(
fd
,
STDERR_FILENO
);
...
...
@@ -123,7 +123,7 @@ static void main_daemon() {
// Loop forever to listen for requests
while
(
1
)
{
int
*
client
=
xmalloc
(
sizeof
(
int
))
;
int
*
client
=
new
int
;
*
client
=
xaccept4
(
fd
,
NULL
,
NULL
,
SOCK_CLOEXEC
);
pthread_t
thread
;
xpthread_create
(
&
thread
,
NULL
,
request_handler
,
client
);
...
...
native/jni/daemon/db.c
→
native/jni/daemon/db.c
pp
View file @
cda57dd4
...
...
@@ -10,8 +10,8 @@
#define DB_VERSION 7
static
int
ver_cb
(
void
*
v
,
int
col_num
,
char
**
data
,
char
**
col_name
)
{
*
((
int
*
)
v
)
=
atoi
(
data
[
0
]);
static
int
ver_cb
(
void
*
v
er
,
int
,
char
**
data
,
char
**
)
{
*
((
int
*
)
v
er
)
=
atoi
(
data
[
0
]);
return
0
;
}
...
...
@@ -19,7 +19,7 @@ static int ver_cb(void *v, int col_num, char **data, char **col_name) {
if (err) { \
LOGE("sqlite3_exec: %s\n", err); \
sqlite3_free(err); \
return
NULL
; \
return
nullptr
; \
}
static
sqlite3
*
open_and_init_db
()
{
...
...
@@ -27,7 +27,7 @@ static sqlite3 *open_and_init_db() {
int
ret
=
sqlite3_open
(
MAGISKDB
,
&
db
);
if
(
ret
)
{
LOGE
(
"sqlite3 open failure: %s
\n
"
,
sqlite3_errstr
(
ret
));
return
NULL
;
return
nullptr
;
}
int
ver
,
upgrade
=
0
;
char
*
err
;
...
...
@@ -36,7 +36,7 @@ static sqlite3 *open_and_init_db() {
if
(
ver
>
DB_VERSION
)
{
// Don't support downgrading database
sqlite3_close_v2
(
db
);
return
NULL
;
return
nullptr
;
}
if
(
ver
<
3
)
{
// Policies
...
...
@@ -44,20 +44,20 @@ static sqlite3 *open_and_init_db() {
"CREATE TABLE IF NOT EXISTS policies "
"(uid INT, package_name TEXT, policy INT, until INT, "
"logging INT, notification INT, PRIMARY KEY(uid))"
,
NULL
,
NULL
,
&
err
);
nullptr
,
nullptr
,
&
err
);
err_abort
(
err
);
// Logs
sqlite3_exec
(
db
,
"CREATE TABLE IF NOT EXISTS logs "
"(from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, "
"to_uid INT, action INT, time INT, command TEXT)"
,
NULL
,
NULL
,
&
err
);
nullptr
,
nullptr
,
&
err
);
err_abort
(
err
);
// Settings
sqlite3_exec
(
db
,
"CREATE TABLE IF NOT EXISTS settings "
"(key TEXT, value INT, PRIMARY KEY(key))"
,
NULL
,
NULL
,
&
err
);
nullptr
,
nullptr
,
&
err
);
err_abort
(
err
);
ver
=
3
;
upgrade
=
1
;
...
...
@@ -67,13 +67,13 @@ static sqlite3 *open_and_init_db() {
sqlite3_exec
(
db
,
"CREATE TABLE IF NOT EXISTS strings "
"(key TEXT, value TEXT, PRIMARY KEY(key))"
,
NULL
,
NULL
,
&
err
);
nullptr
,
nullptr
,
&
err
);
err_abort
(
err
);
ver
=
4
;
upgrade
=
1
;
}
if
(
ver
==
4
)
{
sqlite3_exec
(
db
,
"UPDATE policies SET uid=uid%100000"
,
NULL
,
NULL
,
&
err
);
sqlite3_exec
(
db
,
"UPDATE policies SET uid=uid%100000"
,
nullptr
,
nullptr
,
&
err
);
err_abort
(
err
);
/* Skip version 5 */
ver
=
6
;
...
...
@@ -84,7 +84,7 @@ static sqlite3 *open_and_init_db() {
sqlite3_exec
(
db
,
"CREATE TABLE IF NOT EXISTS hidelist "
"(process TEXT, PRIMARY KEY(process))"
,
NULL
,
NULL
,
&
err
);
nullptr
,
nullptr
,
&
err
);
err_abort
(
err
);
ver
=
7
;
upgrade
=
1
;
...
...
@@ -94,7 +94,7 @@ static sqlite3 *open_and_init_db() {
// Set version
char
query
[
32
];
sprintf
(
query
,
"PRAGMA user_version=%d"
,
ver
);
sqlite3_exec
(
db
,
query
,
NULL
,
NULL
,
&
err
);
sqlite3_exec
(
db
,
query
,
nullptr
,
nullptr
,
&
err
);
err_abort
(
err
);
}
return
db
;
...
...
@@ -102,7 +102,7 @@ static sqlite3 *open_and_init_db() {
sqlite3
*
get_magiskdb
()
{
sqlite3
*
db
=
open_and_init_db
();
if
(
db
==
NULL
)
{
if
(
db
==
nullptr
)
{
// Open fails, remove and reconstruct
unlink
(
MAGISKDB
);
db
=
open_and_init_db
();
...
...
@@ -111,7 +111,7 @@ sqlite3 *get_magiskdb() {
}
static
int
settings_cb
(
void
*
v
,
int
col_num
,
char
**
data
,
char
**
col_name
)
{
struct
db_settings
*
dbs
=
v
;
auto
dbs
=
(
db_settings
*
)
v
;
int
key
=
-
1
,
value
;
for
(
int
i
=
0
;
i
<
col_num
;
++
i
)
{
if
(
strcmp
(
col_name
[
i
],
"key"
)
==
0
)
{
...
...
@@ -131,7 +131,7 @@ static int settings_cb(void *v, int col_num, char **data, char **col_name) {
}
int
get_db_settings
(
sqlite3
*
db
,
int
key
,
struct
db_settings
*
dbs
)
{
if
(
db
==
NULL
)
if
(
db
==
nullptr
)
return
1
;
char
*
err
;
if
(
key
>
0
)
{
...
...
@@ -150,7 +150,7 @@ int get_db_settings(sqlite3 *db, int key, struct db_settings *dbs) {
}
static
int
strings_cb
(
void
*
v
,
int
col_num
,
char
**
data
,
char
**
col_name
)
{
struct
db_strings
*
dbs
=
v
;
auto
dbs
=
(
db_strings
*
)
v
;
int
key
=
-
1
;
char
*
value
;
for
(
int
i
=
0
;
i
<
col_num
;
++
i
)
{
...
...
@@ -171,7 +171,7 @@ static int strings_cb(void *v, int col_num, char **data, char **col_name) {
}
int
get_db_strings
(
sqlite3
*
db
,
int
key
,
struct
db_strings
*
str
)
{
if
(
db
==
NULL
)
if
(
db
==
nullptr
)
return
1
;
char
*
err
;
if
(
key
>
0
)
{
...
...
@@ -190,7 +190,7 @@ int get_db_strings(sqlite3 *db, int key, struct db_strings *str) {
}
static
int
policy_cb
(
void
*
v
,
int
col_num
,
char
**
data
,
char
**
col_name
)
{
struct
su_access
*
su
=
v
;
auto
su
=
(
su_access
*
)
v
;
for
(
int
i
=
0
;
i
<
col_num
;
i
++
)
{
if
(
strcmp
(
col_name
[
i
],
"policy"
)
==
0
)
su
->
policy
=
(
policy_t
)
atoi
(
data
[
i
]);
...
...
@@ -204,11 +204,11 @@ static int policy_cb(void *v, int col_num, char **data, char **col_name) {
}
int
get_uid_policy
(
sqlite3
*
db
,
int
uid
,
struct
su_access
*
su
)
{
if
(
db
==
NULL
)
if
(
db
==
nullptr
)
return
1
;
char
query
[
256
],
*
err
;
sprintf
(
query
,
"SELECT policy, logging, notification FROM policies "
"WHERE uid=%d AND (until=0 OR until>%li)"
,
uid
,
time
(
NULL
));
"WHERE uid=%d AND (until=0 OR until>%li)"
,
uid
,
time
(
nullptr
));
sqlite3_exec
(
db
,
query
,
policy_cb
,
su
,
&
err
);
if
(
err
)
{
LOGE
(
"sqlite3_exec: %s
\n
"
,
err
);
...
...
@@ -219,7 +219,7 @@ int get_uid_policy(sqlite3 *db, int uid, struct su_access *su) {
}
int
validate_manager
(
char
*
alt_pkg
,
int
userid
,
struct
stat
*
st
)
{
if
(
st
==
NULL
)
{
if
(
st
==
nullptr
)
{
struct
stat
stat
;
st
=
&
stat
;
}
...
...
@@ -229,7 +229,7 @@ int validate_manager(char *alt_pkg, int userid, struct stat *st) {
sprintf
(
app_path
,
"%s/%d/%s"
,
base
,
userid
,
alt_pkg
[
0
]
?
alt_pkg
:
"xxx"
);
if
(
stat
(
app_path
,
st
))
{
// Check the official package name
sprintf
(
app_path
,
"%s/%d/"
JAVA_PACKAGE_NAME
,
base
,
userid
);
sprintf
(
app_path
,
"%s/%d/"
JAVA_PACKAGE_NAME
,
base
,
userid
);
if
(
stat
(
app_path
,
st
))
{
LOGE
(
"su: cannot find manager"
);
memset
(
st
,
0
,
sizeof
(
*
st
));
...
...
@@ -256,7 +256,7 @@ int exec_sql(const char *sql) {
sqlite3
*
db
=
get_magiskdb
();
if
(
db
)
{
char
*
err
;
sqlite3_exec
(
db
,
sql
,
print_cb
,
NULL
,
&
err
);
sqlite3_exec
(
db
,
sql
,
print_cb
,
nullptr
,
&
err
);
sqlite3_close_v2
(
db
);
if
(
err
)
{
fprintf
(
stderr
,
"sql_err: %s
\n
"
,
err
);
...
...
native/jni/daemon/log_daemon.c
→
native/jni/daemon/log_daemon.c
pp
View file @
cda57dd4
...
...
@@ -19,7 +19,7 @@
#include "flags.h"
int
log_daemon_started
=
0
;
static
struct
vector
log_cmd
,
clear_cmd
;
static
Array
<
const
char
*>
log_cmd
,
clear_cmd
;
static
pthread_mutex_t
lock
=
PTHREAD_MUTEX_INITIALIZER
;
enum
{
...
...
@@ -33,7 +33,7 @@ struct log_listener {
};
static
int
am_proc_start_filter
(
const
char
*
log
)
{
return
strstr
(
log
,
"am_proc_start"
)
!=
NULL
;
return
strstr
(
log
,
"am_proc_start"
)
!=
nullptr
;
}
static
int
magisk_log_filter
(
const
char
*
log
)
{
...
...
@@ -52,17 +52,17 @@ static struct log_listener events[] = {
};
#define EVENT_NUM (sizeof(events) / sizeof(struct log_listener))
static
void
sigpipe_handler
(
int
sig
)
{
static
void
sigpipe_handler
(
int
)
{
close
(
events
[
HIDE_EVENT
].
fd
);
events
[
HIDE_EVENT
].
fd
=
-
1
;
}
static
void
*
monitor_thread
(
void
*
args
)
{
static
void
*
monitor_thread
(
void
*
)
{
// Block SIGPIPE to prevent interruption
sigset_t
block_set
;
sigemptyset
(
&
block_set
);
sigaddset
(
&
block_set
,
SIGPIPE
);
pthread_sigmask
(
SIG_SETMASK
,
&
block_set
,
NULL
);
pthread_sigmask
(
SIG_SETMASK
,
&
block_set
,
nullptr
);
// Give the main daemon some time before we monitor it
sleep
(
5
);
int
fd
;
...
...
@@ -77,12 +77,12 @@ static void *monitor_thread(void *args) {
}
}
static
void
*
logcat_thread
(
void
*
args
)
{
static
void
*
logcat_thread
(
void
*
)
{
int
log_fd
=
-
1
,
log_pid
;
char
line
[
4096
];
while
(
1
)
{
// Start logcat
log_pid
=
exec_array
(
0
,
&
log_fd
,
NULL
,
(
const
char
**
)
vec_entry
(
&
log_cmd
));
log_pid
=
exec_array
(
0
,
&
log_fd
,
nullptr
,
log_cmd
.
data
(
));
FILE
*
logs
=
fdopen
(
log_fd
,
"r"
);
while
(
fgets
(
line
,
sizeof
(
line
),
logs
))
{
if
(
line
[
0
]
==
'-'
)
...
...
@@ -99,12 +99,12 @@ static void *logcat_thread(void *args) {
fclose
(
logs
);
log_fd
=
-
1
;
kill
(
log_pid
,
SIGTERM
);
waitpid
(
log_pid
,
NULL
,
0
);
waitpid
(
log_pid
,
nullptr
,
0
);
LOGI
(
"magisklogd: logcat output EOF"
);
// Clear buffer
log_pid
=
exec_array
(
0
,
NULL
,
NULL
,
(
const
char
**
)
vec_entry
(
&
clear_cmd
));
waitpid
(
log_pid
,
NULL
,
0
);
log_pid
=
exec_array
(
0
,
nullptr
,
nullptr
,
clear_cmd
.
data
(
));
waitpid
(
log_pid
,
nullptr
,
0
);
}
}
...
...
@@ -117,36 +117,42 @@ static void log_daemon() {
struct
sigaction
act
;
memset
(
&
act
,
0
,
sizeof
(
act
));
act
.
sa_handler
=
sigpipe_handler
;
sigaction
(
SIGPIPE
,
&
act
,
NULL
);
sigaction
(
SIGPIPE
,
&
act
,
nullptr
);
// Setup log dumps
rename
(
LOGFILE
,
LOGFILE
".bak"
);
events
[
LOG_EVENT
].
fd
=
xopen
(
LOGFILE
,
O_CREAT
|
O_WRONLY
|
O_TRUNC
|
O_CLOEXEC
|
O_APPEND
,
0644
);
// Construct cmdline
vec_init
(
&
log_cmd
);
vec_push_back
(
&
log_cmd
,
MIRRDIR
"/system/bin/logcat"
);
log_cmd
.
push_back
(
MIRRDIR
"/system/bin/logcat"
);
// Test whether these buffers actually works
const
char
*
b
[]
=
{
"main"
,
"events"
,
"crash"
};
for
(
int
i
=
0
;
i
<
3
;
++
i
)
{
if
(
exec_command_sync
(
MIRRDIR
"/system/bin/logcat"
,
"-b"
,
b
[
i
],
"-d"
,
"-f"
,
"/dev/null"
,
NULL
)
==
0
)
vec_push_back_all
(
&
log_cmd
,
"-b"
,
b
[
i
],
NULL
);
if
(
exec_command_sync
(
MIRRDIR
"/system/bin/logcat"
,
"-b"
,
b
[
i
],
"-d"
,
"-f"
,
"/dev/null"
,
nullptr
)
==
0
)
{
log_cmd
.
push_back
(
"-b"
);
log_cmd
.
push_back
(
b
[
i
]);
}
}
chmod
(
"/dev/null"
,
0666
);
vec_dup
(
&
log_cmd
,
&
clear_cmd
);
vec_push_back_all
(
&
log_cmd
,
"-v"
,
"threadtime"
,
"-s"
,
"am_proc_start"
,
"Magisk"
,
NULL
);
clear_cmd
=
log_cmd
;
log_cmd
.
push_back
(
"-v"
);
log_cmd
.
push_back
(
"threadtime"
);
log_cmd
.
push_back
(
"-s"
);
log_cmd
.
push_back
(
"am_proc_start"
);
log_cmd
.
push_back
(
"Magisk"
);
#ifdef MAGISK_DEBUG
vec_push_back
(
&
log_cmd
,
"*:F"
);
log_cmd
.
push_back
(
"*:F"
);
#endif
vec_push_back
(
&
log_cmd
,
NULL
);
vec_push_back
(
&
clear_cmd
,
"-c"
);
vec_push_back
(
&
clear_cmd
,
NULL
);
log_cmd
.
push_back
(
nullptr
);
clear_cmd
.
push_back
(
"-c"
);
clear_cmd
.
push_back
(
nullptr
);
// Start worker threads
pthread_t
thread
;
pthread_create
(
&
thread
,
NULL
,
monitor_thread
,
NULL
);
pthread_create
(
&
thread
,
nullptr
,
monitor_thread
,
nullptr
);
pthread_detach
(
thread
);
xpthread_create
(
&
thread
,
NULL
,
logcat_thread
,
NULL
);
xpthread_create
(
&
thread
,
nullptr
,
logcat_thread
,
nullptr
);
pthread_detach
(
thread
);
// Handle socket requests
...
...
@@ -157,7 +163,7 @@ static void log_daemon() {
exit
(
1
);
xlisten
(
sockfd
,
10
);
while
(
1
)
{
int
fd
=
xaccept4
(
sockfd
,
NULL
,
NULL
,
SOCK_CLOEXEC
);
int
fd
=
xaccept4
(
sockfd
,
nullptr
,
nullptr
,
SOCK_CLOEXEC
);
switch
(
read_int
(
fd
))
{
case
HIDE_CONNECT
:
pthread_mutex_lock
(
&
lock
);
...
...
@@ -175,7 +181,7 @@ static void log_daemon() {
int
start_log_daemon
()
{
if
(
!
log_daemon_started
)
{
if
(
exec_command_sync
(
MIRRDIR
"/system/bin/logcat"
,
"-d"
,
"-f"
,
"/dev/null"
,
NULL
)
==
0
)
{
if
(
exec_command_sync
(
MIRRDIR
"/system/bin/logcat"
,
"-d"
,
"-f"
,
"/dev/null"
,
nullptr
)
==
0
)
{
if
(
fork_dont_care
()
==
0
)
log_daemon
();
log_daemon_started
=
1
;
...
...
native/jni/daemon/magisk.c
→
native/jni/daemon/magisk.c
pp
View file @
cda57dd4
...
...
@@ -26,7 +26,7 @@ static int create_links(const char *bin, const char *path) {
return
ret
;
}
static
void
usage
()
{
[[
noreturn
]]
static
void
usage
()
{
fprintf
(
stderr
,
"Magisk v"
xstr
(
MAGISK_VERSION
)
"("
xstr
(
MAGISK_VER_CODE
)
") (by topjohnwu) multi-call binary
\n
"
"
\n
"
...
...
@@ -117,5 +117,4 @@ int magisk_main(int argc, char *argv[]) {
}
usage
();
return
1
;
}
native/jni/daemon/socket.c
→
native/jni/daemon/socket.c
pp
View file @
cda57dd4
...
...
@@ -50,6 +50,7 @@ int socket_accept(int sockfd, int timeout) {
int
recv_fd
(
int
sockfd
)
{
// Need to receive data from the message, otherwise don't care about it.
char
iovbuf
;
struct
cmsghdr
*
cmsg
;
struct
iovec
iov
=
{
.
iov_base
=
&
iovbuf
,
...
...
@@ -79,7 +80,7 @@ int recv_fd(int sockfd) {
goto
error
;
}
struct
cmsghdr
*
cmsg
=
CMSG_FIRSTHDR
(
&
msg
);
cmsg
=
CMSG_FIRSTHDR
(
&
msg
);
if
(
cmsg
==
NULL
||
cmsg
->
cmsg_len
!=
CMSG_LEN
(
sizeof
(
int
))
||
...
...
@@ -104,8 +105,9 @@ error:
*/
void
send_fd
(
int
sockfd
,
int
fd
)
{
// Need to send some data in the message, this will do.
char
junk
[]
=
{
'\0'
};
struct
iovec
iov
=
{
.
iov_base
=
""
,
.
iov_base
=
junk
,
.
iov_len
=
1
,
};
...
...
@@ -166,7 +168,7 @@ void write_int_be(int fd, int val) {
}
static
char
*
rd_str
(
int
fd
,
int
len
)
{
char
*
val
=
xmalloc
(
sizeof
(
char
)
*
(
len
+
1
));
char
*
val
=
(
char
*
)
xmalloc
(
sizeof
(
char
)
*
(
len
+
1
));
xxread
(
fd
,
val
,
len
);
val
[
len
]
=
'\0'
;
return
val
;
...
...
native/jni/include/db.h
View file @
cda57dd4
...
...
@@ -4,10 +4,6 @@
#include <sqlite3.h>
#include <sys/stat.h>
#ifdef __cplusplus
extern
"C"
{
#endif
/***************
* DB Settings *
***************/
...
...
@@ -19,7 +15,7 @@ extern "C" {
"mnt_ns" \
})
#define DB_SETTINGS_NUM
(sizeof(DB_SETTING_KEYS) / sizeof(char*))
#define DB_SETTINGS_NUM
3
// Settings indices
enum
{
...
...
@@ -52,15 +48,14 @@ enum {
struct
db_settings
{
int
v
[
DB_SETTINGS_NUM
];
db_settings
()
:
v
{
ROOT_ACCESS_APPS_AND_ADB
,
MULTIUSER_MODE_OWNER_ONLY
,
NAMESPACE_MODE_REQUESTER
}
{}
};
#define DEFAULT_DB_SETTINGS \
(struct db_settings) { .v = { \
ROOT_ACCESS_APPS_AND_ADB, \
MULTIUSER_MODE_OWNER_ONLY, \
NAMESPACE_MODE_REQUESTER, \
}}
/**************
* DB Strings *
**************/
...
...
@@ -70,7 +65,7 @@ NAMESPACE_MODE_REQUESTER, \
"requester", \
})
#define DB_STRING_NUM
(sizeof(DB_STRING_KEYS) / sizeof(char*))
#define DB_STRING_NUM
1
// Strings indices
enum
{
...
...
@@ -79,6 +74,10 @@ enum {
struct
db_strings
{
char
s
[
DB_STRING_NUM
][
128
];
db_strings
()
{
for
(
int
i
=
0
;
i
<
DB_STRING_NUM
;
++
i
)
s
[
i
][
0
]
=
'\0'
;
}
};
/*************
...
...
@@ -97,19 +96,19 @@ struct su_access {
int
notify
;
};
#define DEFAULT_SU_ACCESS (s
truct s
u_access) { \
#define DEFAULT_SU_ACCESS (su_access) { \
.policy = QUERY, \
.log = 1, \
.notify = 1 \
}
#define SILENT_SU_ACCESS (s
truct s
u_access) { \
#define SILENT_SU_ACCESS (su_access) { \
.policy = ALLOW, \
.log = 0, \
.notify = 0 \
}
#define NO_SU_ACCESS (s
truct s
u_access) { \
#define NO_SU_ACCESS (su_access) { \
.policy = DENY, \
.log = 0, \
.notify = 0 \
...
...
@@ -126,8 +125,4 @@ int get_uid_policy(sqlite3 *db, int uid, struct su_access *su);
int
validate_manager
(
char
*
alt_pkg
,
int
userid
,
struct
stat
*
st
);
int
exec_sql
(
const
char
*
sql
);
#ifdef __cplusplus
}
#endif
#endif //DB_H
native/jni/include/img.h
View file @
cda57dd4
#ifndef IMG_H
#define IMG_H
#ifdef __cplusplus
extern
"C"
{
#endif
int
create_img
(
const
char
*
img
,
int
size
);
int
resize_img
(
const
char
*
img
,
int
size
);
char
*
mount_image
(
const
char
*
img
,
const
char
*
target
);
...
...
@@ -8,4 +12,8 @@ int umount_image(const char *target, const char *device);
int
merge_img
(
const
char
*
source
,
const
char
*
target
);
int
trim_img
(
const
char
*
img
,
const
char
*
mount
,
char
*
loop
);
#ifdef __cplusplus
}
#endif
#endif //IMG_H
native/jni/include/magisk.h
View file @
cda57dd4
...
...
@@ -45,8 +45,8 @@ extern "C" {
extern
char
*
argv0
;
/* For changing process name */
#define applet_names
((
char *[]) { "magisk", "su", "resetprop", "magiskhide", "imgtool", NULL })
#define init_applet
((
char *[]) { "magiskpolicy", "supolicy", NULL })
#define applet_names
((const
char *[]) { "magisk", "su", "resetprop", "magiskhide", "imgtool", NULL })
#define init_applet
((const
char *[]) { "magiskpolicy", "supolicy", NULL })
extern
int
(
*
applet_main
[])
(
int
,
char
*
[]),
(
*
init_applet_main
[])
(
int
,
char
*
[]);
...
...
native/jni/include/resetprop.h
View file @
cda57dd4
/* resetprop.h - API for resetprop
*/
#ifndef _RESETPROP_H_
#define _RESETPROP_H_
#ifdef __cplusplus
extern
"C"
{
#endif
#pragma once
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
);
char
*
getprop2
(
const
char
*
name
,
int
persist
);
int
deleteprop
(
const
char
*
name
);
int
deleteprop2
(
const
char
*
name
,
const
int
persist
);
int
read_prop_file
(
const
char
*
filename
,
const
int
trigger
);
void
getprop_all
(
void
(
*
callback
)(
const
char
*
,
const
char
*
,
void
*
),
void
*
cookie
,
int
persist
);
#ifdef __cplusplus
}
#endif
int
setprop
(
const
char
*
name
,
const
char
*
value
,
const
bool
trigger
=
true
);
char
*
getprop
(
const
char
*
name
,
bool
persist
=
false
);
void
getprop
(
void
(
*
callback
)(
const
char
*
,
const
char
*
,
void
*
),
void
*
cookie
,
bool
persist
=
false
);
int
deleteprop
(
const
char
*
name
,
bool
persist
=
false
);
int
load_prop_file
(
const
char
*
filename
,
const
bool
trigger
=
true
);
#endif
native/jni/magiskhide/hide_utils.cpp
View file @
cda57dd4
...
...
@@ -51,7 +51,7 @@ void hide_sensitive_props() {
value
=
getprop
(
prop_key
[
i
]);
if
(
value
)
{
if
(
strcmp
(
value
,
prop_value
[
i
])
!=
0
)
setprop
2
(
prop_key
[
i
],
prop_value
[
i
],
0
);
setprop
(
prop_key
[
i
],
prop_value
[
i
],
false
);
free
(
value
);
}
}
...
...
@@ -133,10 +133,10 @@ static void kill_process(const char *name) {
void
clean_magisk_props
()
{
LOGD
(
"hide_utils: Cleaning magisk props
\n
"
);
getprop
_all
([](
const
char
*
name
,
auto
,
auto
)
->
void
{
getprop
([](
const
char
*
name
,
auto
,
auto
)
->
void
{
if
(
strstr
(
name
,
"magisk"
))
deleteprop
2
(
name
,
0
);
},
nullptr
,
0
);
deleteprop
(
name
);
},
nullptr
,
false
);
}
static
int
add_list
(
sqlite3
*
db
,
char
*
proc
)
{
...
...
native/jni/magiskhide/magiskhide.cpp
View file @
cda57dd4
...
...
@@ -39,14 +39,14 @@ int launch_magiskhide() {
if
(
!
log_daemon_started
)
{
setprop
(
MAGISKHIDE_PROP
,
"0"
);
// Remove without actually removing persist props
deleteprop
2
(
MAGISKHIDE_PROP
,
0
);
deleteprop
(
MAGISKHIDE_PROP
);
return
LOGCAT_DISABLED
;
}
hide_enabled
=
1
;
LOGI
(
"* Starting MagiskHide
\n
"
);
deleteprop
2
(
MAGISKHIDE_PROP
,
1
);
deleteprop
(
MAGISKHIDE_PROP
,
true
);
hide_sensitive_props
();
...
...
@@ -77,7 +77,7 @@ int stop_magiskhide() {
hide_enabled
=
0
;
setprop
(
MAGISKHIDE_PROP
,
"0"
);
// Remove without actually removing persist props
deleteprop
2
(
MAGISKHIDE_PROP
,
0
);
deleteprop
(
MAGISKHIDE_PROP
);
pthread_kill
(
proc_monitor_thread
,
TERM_THREAD
);
return
DAEMON_SUCCESS
;
...
...
native/jni/magiskhide/magiskhide.h
View file @
cda57dd4
...
...
@@ -9,9 +9,7 @@
#define TERM_THREAD SIGUSR1
// Daemon entries
extern
"C"
{
int
launch_magiskhide
();
}
int
stop_magiskhide
();
int
add_list
(
int
client
);
int
rm_list
(
int
client
);
...
...
native/jni/misc/applets.c
→
native/jni/misc/applets.c
pp
View file @
cda57dd4
File moved
native/jni/misc/img.c
→
native/jni/misc/img.c
pp
View file @
cda57dd4
/* img.c - All image related functions
/* img.c
pp
- All image related functions
*/
#include <unistd.h>
...
...
native/jni/resetprop/resetprop.cpp
View file @
cda57dd4
...
...
@@ -112,9 +112,9 @@ static int init_resetprop() {
return
0
;
}
static
void
print_props
(
int
persist
)
{
static
void
print_props
(
bool
persist
)
{
auto
prop_list
=
Array
<
prop_t
>
();
getprop
_all
(
collect_props
,
&
prop_list
,
persist
);
getprop
(
collect_props
,
&
prop_list
,
persist
);
prop_list
.
sort
();
for
(
auto
&
prop
:
prop_list
)
printf
(
"[%s]: [%s]
\n
"
,
prop
.
name
,
prop
.
value
);
...
...
@@ -129,12 +129,8 @@ int prop_exist(const char *name) {
return
__system_property_find
(
name
)
!=
nullptr
;
}
char
*
getprop
(
const
char
*
name
)
{
return
getprop2
(
name
,
0
);
}
// Get prop by name, return string (should free manually!)
char
*
getprop
2
(
const
char
*
name
,
int
persist
)
{
char
*
getprop
(
const
char
*
name
,
bool
persist
)
{
if
(
!
check_legal_property_name
(
name
)
||
init_resetprop
())
return
nullptr
;
const
prop_info
*
pi
=
__system_property_find
(
name
);
...
...
@@ -157,7 +153,7 @@ char *getprop2(const char *name, int persist) {
}
}
void
getprop
_all
(
void
(
*
callback
)(
const
char
*
,
const
char
*
,
void
*
),
void
*
cookie
,
int
persist
)
{
void
getprop
(
void
(
*
callback
)(
const
char
*
,
const
char
*
,
void
*
),
void
*
cookie
,
bool
persist
)
{
if
(
init_resetprop
())
return
;
read_cb_t
read_cb
(
callback
,
cookie
);
__system_property_foreach
(
read_props
,
&
read_cb
);
...
...
@@ -167,11 +163,7 @@ void getprop_all(void (*callback)(const char *, const char *, void *), void *coo
}
}
int
setprop
(
const
char
*
name
,
const
char
*
value
)
{
return
setprop2
(
name
,
value
,
1
);
}
int
setprop2
(
const
char
*
name
,
const
char
*
value
,
const
int
trigger
)
{
int
setprop
(
const
char
*
name
,
const
char
*
value
,
const
bool
trigger
)
{
if
(
!
check_legal_property_name
(
name
))
return
1
;
if
(
init_resetprop
())
...
...
@@ -205,11 +197,7 @@ int setprop2(const char *name, const char *value, const int trigger) {
return
ret
;
}
int
deleteprop
(
const
char
*
name
)
{
return
deleteprop2
(
name
,
1
);
}
int
deleteprop2
(
const
char
*
name
,
int
persist
)
{
int
deleteprop
(
const
char
*
name
,
bool
persist
)
{
if
(
!
check_legal_property_name
(
name
))
return
1
;
if
(
init_resetprop
())
return
-
1
;
...
...
@@ -221,7 +209,7 @@ int deleteprop2(const char *name, int persist) {
return
__system_property_del
(
name
)
&&
!
(
persist
&&
strncmp
(
name
,
"persist."
,
8
)
==
0
);
}
int
read_prop_file
(
const
char
*
filename
,
const
int
trigger
)
{
int
load_prop_file
(
const
char
*
filename
,
const
bool
trigger
)
{
if
(
init_resetprop
())
return
-
1
;
LOGD
(
"resetprop: Load prop file [%s]
\n
"
,
filename
);
FILE
*
fp
=
fopen
(
filename
,
"r"
);
...
...
@@ -255,7 +243,7 @@ int read_prop_file(const char* filename, const int trigger) {
if
(
((
pch
==
nullptr
)
||
(
i
>=
(
pch
-
line
)))
||
(
pch
>=
line
+
read
-
1
)
)
continue
;
// Separate the string
*
pch
=
'\0'
;
setprop
2
(
line
+
i
,
pch
+
1
,
trigger
);
setprop
(
line
+
i
,
pch
+
1
,
trigger
);
}
free
(
line
);
fclose
(
fp
);
...
...
@@ -264,8 +252,8 @@ int read_prop_file(const char* filename, const int trigger) {
int
resetprop_main
(
int
argc
,
char
*
argv
[])
{
log_cb
.
d
=
[](
auto
fmt
,
auto
ap
)
->
int
{
return
verbose
?
vfprintf
(
stderr
,
fmt
,
ap
)
:
0
;
};
int
trigger
=
1
,
persist
=
0
;
bool
trigger
=
true
,
persist
=
false
;
char
*
argv0
=
argv
[
0
],
*
prop
;
--
argc
;
...
...
@@ -277,9 +265,9 @@ int resetprop_main(int argc, char *argv[]) {
switch
(
argv
[
0
][
idx
])
{
case
'-'
:
if
(
strcmp
(
argv
[
0
],
"--file"
)
==
0
&&
argc
==
2
)
{
return
re
ad_prop_file
(
argv
[
1
],
trigger
);
return
lo
ad_prop_file
(
argv
[
1
],
trigger
);
}
else
if
(
strcmp
(
argv
[
0
],
"--delete"
)
==
0
&&
argc
==
2
)
{
return
deleteprop
2
(
argv
[
1
],
persist
);
return
deleteprop
(
argv
[
1
],
persist
);
}
else
if
(
strcmp
(
argv
[
0
],
"--help"
)
==
0
)
{
usage
(
argv0
);
}
...
...
@@ -309,13 +297,13 @@ int resetprop_main(int argc, char *argv[]) {
print_props
(
persist
);
return
0
;
case
1
:
prop
=
getprop
2
(
argv
[
0
],
persist
);
prop
=
getprop
(
argv
[
0
],
persist
);
if
(
prop
==
nullptr
)
return
1
;
printf
(
"%s
\n
"
,
prop
);
free
(
prop
);
return
0
;
case
2
:
return
setprop
2
(
argv
[
0
],
argv
[
1
],
trigger
);
return
setprop
(
argv
[
0
],
argv
[
1
],
trigger
);
default:
usage
(
argv0
);
}
...
...
native/jni/su/connect.c
→
native/jni/su/connect.c
pp
View file @
cda57dd4
...
...
@@ -19,7 +19,7 @@
#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am"
static
char
*
get_command
(
const
struct
su_request
*
to
)
{
static
c
onst
c
har
*
get_command
(
const
struct
su_request
*
to
)
{
if
(
to
->
command
[
0
])
return
to
->
command
;
if
(
to
->
shell
[
0
])
...
...
@@ -27,7 +27,7 @@ static char *get_command(const struct su_request *to) {
return
DEFAULT_SHELL
;
}
static
void
silent_run
(
c
har
*
const
args
[])
{
static
void
silent_run
(
c
onst
char
*
args
[])
{
if
(
fork_dont_care
())
return
;
int
zero
=
open
(
"/dev/zero"
,
O_RDONLY
|
O_CLOEXEC
);
...
...
@@ -36,7 +36,7 @@ static void silent_run(char * const args[]) {
xdup2
(
null
,
1
);
xdup2
(
null
,
2
);
setenv
(
"CLASSPATH"
,
"/system/framework/am.jar"
,
1
);
execv
(
args
[
0
],
args
);
execv
(
args
[
0
],
(
char
**
)
args
);
PLOGE
(
"exec am"
);
_exit
(
EXIT_FAILURE
);
}
...
...
@@ -71,7 +71,7 @@ void app_log(struct su_context *ctx) {
char
policy
[
2
];
sprintf
(
policy
,
"%d"
,
ctx
->
info
->
access
.
policy
);
char
*
cmd
[]
=
{
c
onst
c
har
*
cmd
[]
=
{
AM_PATH
,
"broadcast"
,
"-a"
,
"android.intent.action.BOOT_COMPLETED"
,
"-p"
,
DB_STR
(
ctx
->
info
,
SU_MANAGER
),
...
...
@@ -84,7 +84,7 @@ void app_log(struct su_context *ctx) {
"--ei"
,
"policy"
,
policy
,
"--es"
,
"command"
,
get_command
(
&
ctx
->
req
),
"--ez"
,
"notify"
,
ctx
->
info
->
access
.
notify
?
"true"
:
"false"
,
NULL
nullptr
};
silent_run
(
cmd
);
}
...
...
@@ -101,7 +101,7 @@ void app_notify(struct su_context *ctx) {
char
policy
[
2
];
sprintf
(
policy
,
"%d"
,
ctx
->
info
->
access
.
policy
);
char
*
cmd
[]
=
{
c
onst
c
har
*
cmd
[]
=
{
AM_PATH
,
"broadcast"
,
"-a"
,
"android.intent.action.BOOT_COMPLETED"
,
"-p"
,
DB_STR
(
ctx
->
info
,
SU_MANAGER
),
...
...
@@ -110,7 +110,7 @@ void app_notify(struct su_context *ctx) {
"--es"
,
"action"
,
"notify"
,
"--ei"
,
"from.uid"
,
fromUid
,
"--ei"
,
"policy"
,
policy
,
NULL
nullptr
};
silent_run
(
cmd
);
}
...
...
@@ -118,15 +118,15 @@ void app_notify(struct su_context *ctx) {
void
app_connect
(
const
char
*
socket
,
struct
su_info
*
info
)
{
char
user
[
8
];
setup_user
(
user
,
info
);
char
*
cmd
[]
=
{
c
onst
c
har
*
cmd
[]
=
{
AM_PATH
,
"broadcast"
,
"-a"
,
"android.intent.action.BOOT_COMPLETED"
,
"-p"
,
DB_STR
(
info
,
SU_MANAGER
),
"-f"
,
"0x00000020"
,
"--user"
,
user
,
"--es"
,
"action"
,
"request"
,
"--es"
,
"socket"
,
(
char
*
)
socket
,
NULL
"--es"
,
"socket"
,
socket
,
nullptr
};
silent_run
(
cmd
);
}
...
...
@@ -135,4 +135,3 @@ void socket_send_request(int fd, struct su_info *info) {
write_key_token
(
fd
,
"uid"
,
info
->
uid
);
write_string_be
(
fd
,
"eof"
);
}
native/jni/su/pts.c
→
native/jni/su/pts.c
pp
View file @
cda57dd4
...
...
@@ -9,7 +9,6 @@
* helper functions to handle raw input mode and terminal window resizing
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
...
...
native/jni/su/su.c
→
native/jni/su/su.c
pp
View file @
cda57dd4
...
...
@@ -8,7 +8,6 @@
/* su.c - The main function running in the daemon
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
...
...
@@ -95,6 +94,12 @@ static void setup_sighandlers(void (*handler)(int)) {
}
}
// Default values
su_req_base
::
su_req_base
()
:
uid
(
UID_ROOT
),
login
(
false
),
keepenv
(
false
),
mount_master
(
false
)
{}
su_request
::
su_request
()
:
shell
(
DEFAULT_SHELL
),
command
(
""
)
{}
/*
* Connect daemon, send argc, argv, cwd, pts slave
*/
...
...
@@ -112,15 +117,7 @@ int su_client_main(int argc, char *argv[]) {
{
NULL
,
0
,
NULL
,
0
},
};
struct
su_request
su_req
=
{
.
uid
=
UID_ROOT
,
.
login
=
0
,
.
keepenv
=
0
,
.
mount_master
=
0
,
.
shell
=
DEFAULT_SHELL
,
.
command
=
""
,
};
su_request
su_req
;
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
{
// Replace -cn with -z, -mm with -M for supporting getopt_long
...
...
@@ -140,11 +137,11 @@ int su_client_main(int argc, char *argv[]) {
usage
(
EXIT_SUCCESS
);
break
;
case
'l'
:
su_req
.
login
=
1
;
su_req
.
login
=
true
;
break
;
case
'm'
:
case
'p'
:
su_req
.
keepenv
=
1
;
su_req
.
keepenv
=
true
;
break
;
case
's'
:
su_req
.
shell
=
optarg
;
...
...
@@ -159,7 +156,7 @@ int su_client_main(int argc, char *argv[]) {
// Do nothing, placed here for legacy support :)
break
;
case
'M'
:
su_req
.
mount_master
=
1
;
su_req
.
mount_master
=
true
;
break
;
default
:
/* Bionic getopt_long doesn't terminate its error output by newline */
...
...
@@ -169,7 +166,7 @@ int su_client_main(int argc, char *argv[]) {
}
if
(
optind
<
argc
&&
strcmp
(
argv
[
optind
],
"-"
)
==
0
)
{
su_req
.
login
=
1
;
su_req
.
login
=
true
;
optind
++
;
}
/* username or uid */
...
...
@@ -200,7 +197,7 @@ int su_client_main(int argc, char *argv[]) {
}
// Send su_request
xwrite
(
fd
,
&
su_req
,
4
*
sizeof
(
unsigned
));
xwrite
(
fd
,
&
su_req
,
sizeof
(
su_req_base
));
write_string
(
fd
,
su_req
.
shell
);
write_string
(
fd
,
su_req
.
command
);
...
...
@@ -242,4 +239,4 @@ int su_client_main(int argc, char *argv[]) {
close
(
fd
);
return
code
;
}
\ No newline at end of file
}
native/jni/su/su.h
View file @
cda57dd4
...
...
@@ -17,9 +17,9 @@
#define ATTY_OUT 2
#define ATTY_ERR 4
struct
su_info
{
class
su_info
{
public
:
unsigned
uid
;
/* Unique key to find su_info */
pthread_mutex_t
lock
;
/* Internal lock */
int
count
;
/* Just a count for debugging purpose */
/* These values should be guarded with internal lock */
...
...
@@ -31,19 +31,33 @@ struct su_info {
/* These should be guarded with global cache lock */
int
ref
;
int
life
;
su_info
(
unsigned
uid
);
~
su_info
();
void
lock
();
void
unlock
();
private
:
pthread_mutex_t
_lock
;
/* Internal lock */
};
#define DB_SET(i, e) (i)->dbs.v[e]
#define DB_STR(i, e) (i)->str.s[e]
struct
su_req
uest
{
struct
su_req
_base
{
unsigned
uid
;
unsigned
login
;
unsigned
keepenv
;
unsigned
mount_master
;
char
*
shell
;
char
*
command
;
};
bool
login
;
bool
keepenv
;
bool
mount_master
;
protected
:
su_req_base
();
}
__attribute__
((
packed
));
struct
su_request
:
public
su_req_base
{
const
char
*
shell
;
const
char
*
command
;
su_request
();
}
__attribute__
((
packed
));
struct
su_context
{
struct
su_info
*
info
;
...
...
native/jni/su/su_daemon.c
→
native/jni/su/su_daemon.c
pp
View file @
cda57dd4
/* su_daemon.c - The entrypoint for su, connect to daemon and send correct info
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
...
...
@@ -25,32 +20,45 @@
#define TIMEOUT 3
#define LOCK_CACHE() pthread_mutex_lock(&cache_lock)
#define LOCK_INFO() pthread_mutex_lock(&info->lock)
#define UNLOCK_CACHE() pthread_mutex_unlock(&cache_lock)
#define UNLOCK_INFO() pthread_mutex_unlock(&info->lock)
static
pthread_mutex_t
cache_lock
=
PTHREAD_MUTEX_INITIALIZER
;
static
struct
su_info
*
cache
;
static
su_info
*
cache
;
su_info
::
su_info
(
unsigned
uid
)
:
uid
(
uid
),
access
(
DEFAULT_SU_ACCESS
),
_lock
(
PTHREAD_MUTEX_INITIALIZER
),
count
(
0
),
ref
(
0
),
life
(
0
),
mgr_st
({})
{}
su_info
::~
su_info
()
{
pthread_mutex_destroy
(
&
_lock
);
}
void
su_info
::
lock
()
{
pthread_mutex_lock
(
&
_lock
);
}
void
su_info
::
unlock
()
{
pthread_mutex_unlock
(
&
_lock
);
}
static
void
*
info_collector
(
void
*
node
)
{
s
truct
su_info
*
info
=
node
;
s
u_info
*
info
=
(
su_info
*
)
node
;
while
(
1
)
{
sleep
(
1
);
if
(
info
->
life
)
{
LOCK_CACHE
();
if
(
--
info
->
life
==
0
&&
cache
&&
info
->
uid
==
cache
->
uid
)
cache
=
NULL
;
cache
=
nullptr
;
UNLOCK_CACHE
();
}
if
(
!
info
->
life
&&
!
info
->
ref
)
{
pthread_mutex_destroy
(
&
info
->
lock
);
free
(
info
);
return
NULL
;
delete
info
;
return
nullptr
;
}
}
}
static
void
database_check
(
s
truct
s
u_info
*
info
)
{
static
void
database_check
(
su_info
*
info
)
{
int
uid
=
info
->
uid
;
sqlite3
*
db
=
get_magiskdb
();
if
(
db
)
{
...
...
@@ -84,20 +92,16 @@ static void database_check(struct su_info *info) {
}
static
struct
su_info
*
get_su_info
(
unsigned
uid
)
{
s
truct
s
u_info
*
info
;
int
cache_miss
=
0
;
su_info
*
info
;
bool
cache_miss
=
false
;
LOCK_CACHE
();
if
(
cache
&&
cache
->
uid
==
uid
)
{
info
=
cache
;
}
else
{
cache_miss
=
1
;
info
=
xcalloc
(
1
,
sizeof
(
*
info
));
info
->
uid
=
uid
;
info
->
dbs
=
DEFAULT_DB_SETTINGS
;
info
->
access
=
DEFAULT_SU_ACCESS
;
pthread_mutex_init
(
&
info
->
lock
,
NULL
);
cache_miss
=
true
;
info
=
new
su_info
(
uid
);
cache
=
info
;
}
...
...
@@ -108,7 +112,7 @@ static struct su_info *get_su_info(unsigned uid) {
// Start a thread to maintain the cache
if
(
cache_miss
)
{
pthread_t
thread
;
xpthread_create
(
&
thread
,
NULL
,
info_collector
,
info
);
xpthread_create
(
&
thread
,
nullptr
,
info_collector
,
info
);
pthread_detach
(
thread
);
}
...
...
@@ -117,7 +121,7 @@ static struct su_info *get_su_info(unsigned uid) {
LOGD
(
"su: request from uid=[%d] (#%d)
\n
"
,
info
->
uid
,
++
info
->
count
);
// Lock before the policy is determined
LOCK_INFO
();
info
->
lock
();
if
(
info
->
access
.
policy
==
QUERY
)
{
// Not cached, get data from database
...
...
@@ -173,14 +177,14 @@ static struct su_info *get_su_info(unsigned uid) {
}
else
{
socket_send_request
(
fd
,
info
);
int
ret
=
read_int_be
(
fd
);
info
->
access
.
policy
=
ret
<
0
?
DENY
:
ret
;
info
->
access
.
policy
=
ret
<
0
?
DENY
:
static_cast
<
policy_t
>
(
ret
)
;
close
(
fd
);
}
close
(
sockfd
);
}
// Unlock
UNLOCK_INFO
();
info
->
unlock
();
return
info
;
}
...
...
@@ -224,7 +228,7 @@ static void set_identity(unsigned uid) {
void
su_daemon_handler
(
int
client
,
struct
ucred
*
credential
)
{
LOGD
(
"su: request from client: %d
\n
"
,
client
);
s
truct
s
u_info
*
info
=
get_su_info
(
credential
->
uid
);
su_info
*
info
=
get_su_info
(
credential
->
uid
);
// Fail fast
if
(
info
->
access
.
policy
==
DENY
&&
DB_STR
(
info
,
SU_MANAGER
)[
0
]
==
'\0'
)
{
...
...
@@ -289,7 +293,7 @@ void su_daemon_handler(int client, struct ucred *credential) {
}
// Read su_request
xxread
(
client
,
&
ctx
.
req
,
4
*
sizeof
(
unsigned
));
xxread
(
client
,
&
ctx
.
req
,
sizeof
(
su_req_base
));
ctx
.
req
.
shell
=
read_string
(
client
);
ctx
.
req
.
command
=
read_string
(
client
);
...
...
@@ -367,7 +371,7 @@ void su_daemon_handler(int client, struct ucred *credential) {
app_notify
(
&
ctx
);
if
(
info
->
access
.
policy
==
ALLOW
)
{
c
har
*
argv
[]
=
{
NULL
,
NULL
,
NULL
,
NULL
};
c
onst
char
*
argv
[]
=
{
nullptr
,
nullptr
,
nullptr
,
nullptr
};
argv
[
0
]
=
ctx
.
req
.
login
?
"-"
:
ctx
.
req
.
shell
;
...
...
@@ -381,7 +385,7 @@ void su_daemon_handler(int client, struct ucred *credential) {
populate_environment
(
&
ctx
.
req
);
set_identity
(
ctx
.
req
.
uid
);
execvp
(
ctx
.
req
.
shell
,
argv
);
execvp
(
ctx
.
req
.
shell
,
(
char
**
)
argv
);
fprintf
(
stderr
,
"Cannot execute %s: %s
\n
"
,
ctx
.
req
.
shell
,
strerror
(
errno
));
PLOGE
(
"exec"
);
exit
(
EXIT_FAILURE
);
...
...
@@ -391,4 +395,3 @@ void su_daemon_handler(int client, struct ucred *credential) {
exit
(
EXIT_FAILURE
);
}
}
native/jni/utils/Android.mk
View file @
cda57dd4
...
...
@@ -9,7 +9,6 @@ LOCAL_SRC_FILES := \
selinux.cpp \
logging.cpp \
xwrap.cpp \
vector.c \
legacy.c
vector.c
include $(BUILD_STATIC_LIBRARY)
native/jni/utils/include/array.h
View file @
cda57dd4
...
...
@@ -66,6 +66,15 @@ public:
T
*
_node
;
};
T
&
operator
=
(
const
T
&
a
)
{
_size
=
a
.
_size
;
_capacity
=
a
.
_capacity
;
_data
=
new
T
[
_capacity
];
for
(
int
i
=
0
;
i
<
_size
;
++
i
)
_data
[
i
]
=
(
T
&&
)
a
[
i
];
return
*
this
;
}
iterator
begin
()
const
{
return
iterator
(
_data
);
}
iterator
end
()
const
{
return
iterator
(
_data
+
_size
);
}
...
...
@@ -149,7 +158,8 @@ private:
_capacity
*=
2
;
T
*
temp
=
_data
;
_data
=
new
T
[
_capacity
];
for
(
int
i
=
0
;
i
<
_size
;
++
i
)
_data
[
i
]
=
(
T
&&
)
temp
[
i
];
for
(
int
i
=
0
;
i
<
_size
;
++
i
)
_data
[
i
]
=
(
T
&&
)
temp
[
i
];
delete
[]
temp
;
}
};
...
...
native/jni/utils/include/selinux.h
View file @
cda57dd4
...
...
@@ -4,7 +4,6 @@
extern
"C"
{
#endif
extern
void
(
*
freecon
)(
char
*
con
);
extern
int
(
*
setcon
)(
const
char
*
con
);
extern
int
(
*
getfilecon
)(
const
char
*
path
,
char
**
con
);
...
...
native/jni/utils/include/utils.h
View file @
cda57dd4
...
...
@@ -18,8 +18,6 @@ int file_to_array(const char* filename, Array<char *> &arr);
extern
"C"
{
#endif
#include "vector.h"
#define UID_SHELL (get_shell_uid())
#define UID_ROOT 0
#define UID_SYSTEM (get_system_uid())
...
...
@@ -86,8 +84,6 @@ int xpoll(struct pollfd *fds, nfds_t nfds, int timeout);
unsigned
get_shell_uid
();
unsigned
get_system_uid
();
unsigned
get_radio_uid
();
int
file_to_vector
(
const
char
*
filename
,
struct
vector
*
v
);
int
vector_to_file
(
const
char
*
filename
,
struct
vector
*
v
);
ssize_t
fdgets
(
char
*
buf
,
size_t
size
,
int
fd
);
int
is_num
(
const
char
*
s
);
int
exec_array
(
int
err
,
int
*
fd
,
void
(
*
cb
)(
void
),
const
char
*
argv
[]);
...
...
native/jni/utils/legacy.c
deleted
100644 → 0
View file @
4351de50
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "utils.h"
#include "logging.h"
/* All the string should be freed manually!! */
int
file_to_vector
(
const
char
*
filename
,
struct
vector
*
v
)
{
if
(
access
(
filename
,
R_OK
)
!=
0
)
return
1
;
char
*
line
=
NULL
;
size_t
len
=
0
;
ssize_t
read
;
FILE
*
fp
=
xfopen
(
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
;
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment