mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-25 05:47:38 +00:00
Fuse busybox into update-binary and remove from Magisk Manager APK
This commit is contained in:
parent
5fab15fee5
commit
b37bad35c2
@ -1 +1 @@
|
|||||||
Subproject commit 3550d1e61cba0cf482e1d3cf8e92b0f74aae38f9
|
Subproject commit 8195a4d6161dd16d67ece6e643670ae9d3118cdb
|
101
build.py
101
build.py
@ -35,6 +35,8 @@ import zipfile
|
|||||||
import datetime
|
import datetime
|
||||||
import errno
|
import errno
|
||||||
import shutil
|
import shutil
|
||||||
|
import gzip
|
||||||
|
import base64
|
||||||
|
|
||||||
def silentremove(file):
|
def silentremove(file):
|
||||||
try:
|
try:
|
||||||
@ -58,11 +60,8 @@ def build_all(args):
|
|||||||
def build_binary(args):
|
def build_binary(args):
|
||||||
header('* Building Magisk binaries')
|
header('* Building Magisk binaries')
|
||||||
|
|
||||||
# Force update Android.mk footprint to trigger recompilation
|
# Force update Android.mk timestamp to trigger recompilation
|
||||||
with open(os.path.join('jni', 'Android.mk'), 'r') as makefile:
|
os.utime(os.path.join('jni', 'Android.mk'))
|
||||||
content = makefile.read()
|
|
||||||
with open(os.path.join('jni', 'Android.mk'), 'w') as makefile:
|
|
||||||
makefile.write(content)
|
|
||||||
|
|
||||||
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
|
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
|
||||||
debug_flag = '' if args.release else '-DMAGISK_DEBUG'
|
debug_flag = '' if args.release else '-DMAGISK_DEBUG'
|
||||||
@ -74,17 +73,6 @@ def build_binary(args):
|
|||||||
def build_apk(args):
|
def build_apk(args):
|
||||||
header('* Building Magisk Manager')
|
header('* Building Magisk Manager')
|
||||||
|
|
||||||
for arch in ['armeabi-v7a', 'x86']:
|
|
||||||
source = os.path.join('libs', arch, 'busybox')
|
|
||||||
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'jniLibs', arch)
|
|
||||||
if not os.path.exists(source):
|
|
||||||
error('{} does not exist! Please build \'binary\' before building apk'.format(source))
|
|
||||||
if not os.path.exists(target):
|
|
||||||
os.makedirs(target)
|
|
||||||
target = os.path.join(target, 'libbusybox.so')
|
|
||||||
print('cp: {} -> {}'.format(source, target))
|
|
||||||
shutil.copyfile(source, target)
|
|
||||||
|
|
||||||
for key in ['public.certificate.x509.pem', 'private.key.pk8']:
|
for key in ['public.certificate.x509.pem', 'private.key.pk8']:
|
||||||
source = os.path.join('ziptools', key)
|
source = os.path.join('ziptools', key)
|
||||||
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', key)
|
target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', key)
|
||||||
@ -174,11 +162,40 @@ def sign_adjust_zip(unsigned, output):
|
|||||||
silentremove('tmp_signed.zip')
|
silentremove('tmp_signed.zip')
|
||||||
silentremove('tmp_adjusted.zip')
|
silentremove('tmp_adjusted.zip')
|
||||||
|
|
||||||
|
def gen_update_binary():
|
||||||
|
update_bin = []
|
||||||
|
bb = os.path.join('libs', 'armeabi-v7a', 'busybox')
|
||||||
|
if not os.path.exists(bb):
|
||||||
|
error('{} does not exist! Please build \'binary\' before zipping!'.format(bb))
|
||||||
|
with open(bb, 'rb') as busybox:
|
||||||
|
update_bin.append('#! /sbin/sh\nBB_ARM=')
|
||||||
|
update_bin.append(base64.b64encode(gzip.compress(busybox.read())).decode('ascii'))
|
||||||
|
bb = os.path.join('libs', 'x86', 'busybox')
|
||||||
|
if not os.path.exists(bb):
|
||||||
|
error('{} does not exist! Please build \'binary\' before zipping!'.format(bb))
|
||||||
|
with open(bb, 'rb') as busybox:
|
||||||
|
update_bin.append('\nBB_X86=')
|
||||||
|
update_bin.append(base64.b64encode(gzip.compress(busybox.read())).decode('ascii'))
|
||||||
|
update_bin.append('\n')
|
||||||
|
with open(os.path.join('scripts', 'update_binary.sh'), 'r') as script:
|
||||||
|
update_bin.append(script.read())
|
||||||
|
return ''.join(update_bin)
|
||||||
|
|
||||||
def zip_main(args):
|
def zip_main(args):
|
||||||
header('* Packing Flashable Zip')
|
header('* Packing Flashable Zip')
|
||||||
|
|
||||||
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zipf:
|
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
|
||||||
# Compiled Binaries
|
# META-INF
|
||||||
|
# update-binary
|
||||||
|
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
|
||||||
|
print('zip: ' + target)
|
||||||
|
zipf.writestr(target, gen_update_binary())
|
||||||
|
# updater-script
|
||||||
|
source = os.path.join('scripts', 'flash_script.sh')
|
||||||
|
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
|
||||||
|
zip_with_msg(zipf, source, target)
|
||||||
|
|
||||||
|
# Binaries
|
||||||
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
|
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
|
||||||
for binary in ['magisk', 'magiskboot']:
|
for binary in ['magisk', 'magiskboot']:
|
||||||
source = os.path.join('libs', lib_dir, binary)
|
source = os.path.join('libs', lib_dir, binary)
|
||||||
@ -192,22 +209,6 @@ def zip_main(args):
|
|||||||
zip_with_msg(zipf, source, target)
|
zip_with_msg(zipf, source, target)
|
||||||
|
|
||||||
# Scripts
|
# Scripts
|
||||||
# flash_script.sh
|
|
||||||
source = os.path.join('scripts', 'flash_script.sh')
|
|
||||||
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
|
|
||||||
zip_with_msg(zipf, source, target)
|
|
||||||
# addon.d.sh
|
|
||||||
source = os.path.join('scripts', 'addon.d.sh')
|
|
||||||
target = os.path.join('addon.d', '99-magisk.sh')
|
|
||||||
zip_with_msg(zipf, source, target)
|
|
||||||
# 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
|
# boot_patch.sh
|
||||||
source = os.path.join('scripts', 'boot_patch.sh')
|
source = os.path.join('scripts', 'boot_patch.sh')
|
||||||
target = os.path.join('common', 'boot_patch.sh')
|
target = os.path.join('common', 'boot_patch.sh')
|
||||||
@ -221,6 +222,15 @@ def zip_main(args):
|
|||||||
target = os.path.join('common', 'util_functions.sh')
|
target = os.path.join('common', 'util_functions.sh')
|
||||||
print('zip: ' + source + ' -> ' + target)
|
print('zip: ' + source + ' -> ' + target)
|
||||||
zipf.writestr(target, util_func)
|
zipf.writestr(target, util_func)
|
||||||
|
# addon.d.sh
|
||||||
|
source = os.path.join('scripts', 'addon.d.sh')
|
||||||
|
target = os.path.join('addon.d', '99-magisk.sh')
|
||||||
|
zip_with_msg(zipf, source, target)
|
||||||
|
# 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)
|
||||||
|
|
||||||
# Prebuilts
|
# Prebuilts
|
||||||
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
||||||
source = os.path.join('chromeos', chromeos)
|
source = os.path.join('chromeos', chromeos)
|
||||||
@ -234,8 +244,18 @@ def zip_main(args):
|
|||||||
def zip_uninstaller(args):
|
def zip_uninstaller(args):
|
||||||
header('* Packing Uninstaller Zip')
|
header('* Packing Uninstaller Zip')
|
||||||
|
|
||||||
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zipf:
|
with zipfile.ZipFile('tmp_unsigned.zip', 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
|
||||||
# Compiled Binaries
|
# META-INF
|
||||||
|
# update-binary
|
||||||
|
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
|
||||||
|
print('zip: ' + target)
|
||||||
|
zipf.writestr(target, gen_update_binary())
|
||||||
|
# updater-script
|
||||||
|
source = os.path.join('scripts', 'uninstaller_loader.sh')
|
||||||
|
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
|
||||||
|
zip_with_msg(zipf, source, target)
|
||||||
|
|
||||||
|
# Binaries
|
||||||
for lib_dir, zip_dir in [('arm64-v8a', 'arm64'), ('armeabi-v7a', 'arm'), ('x86', 'x86'), ('x86_64', 'x64')]:
|
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')
|
source = os.path.join('libs', lib_dir, 'magiskboot')
|
||||||
target = os.path.join(zip_dir, 'magiskboot')
|
target = os.path.join(zip_dir, 'magiskboot')
|
||||||
@ -245,6 +265,7 @@ def zip_uninstaller(args):
|
|||||||
target = 'magisk_uninstaller.sh'
|
target = 'magisk_uninstaller.sh'
|
||||||
zip_with_msg(zipf, source, target)
|
zip_with_msg(zipf, source, target)
|
||||||
|
|
||||||
|
# Scripts
|
||||||
# util_functions.sh
|
# util_functions.sh
|
||||||
source = os.path.join('scripts', 'util_functions.sh')
|
source = os.path.join('scripts', 'util_functions.sh')
|
||||||
with open(source, 'r') as script:
|
with open(source, 'r') as script:
|
||||||
@ -255,14 +276,6 @@ def zip_uninstaller(args):
|
|||||||
print('zip: ' + source + ' -> ' + target)
|
print('zip: ' + source + ' -> ' + target)
|
||||||
zipf.writestr(target, util_func)
|
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
|
# Prebuilts
|
||||||
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
||||||
source = os.path.join('chromeos', chromeos)
|
source = os.path.join('chromeos', chromeos)
|
||||||
|
@ -502,13 +502,6 @@ void post_fs_data(int client) {
|
|||||||
|
|
||||||
LOGI("** post-fs-data mode running\n");
|
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
|
// Allocate buffer
|
||||||
if (buf == NULL) buf = xmalloc(PATH_MAX);
|
if (buf == NULL) buf = xmalloc(PATH_MAX);
|
||||||
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
|
if (buf2 == NULL) buf2 = xmalloc(PATH_MAX);
|
||||||
@ -538,6 +531,17 @@ void post_fs_data(int client) {
|
|||||||
goto unblock;
|
goto unblock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Link busybox
|
||||||
|
link_busybox();
|
||||||
|
|
||||||
|
// uninstaller
|
||||||
|
if (access(UNINSTALLER, F_OK) == 0) {
|
||||||
|
close(open(UNBLOCKFILE, O_RDONLY | O_CREAT));
|
||||||
|
bb_path();
|
||||||
|
system("(BOOTMODE=true sh " UNINSTALLER ") &");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int new_img = 0;
|
int new_img = 0;
|
||||||
|
|
||||||
if (access(MAINIMG, F_OK) == -1) {
|
if (access(MAINIMG, F_OK) == -1) {
|
||||||
@ -559,9 +563,6 @@ void post_fs_data(int client) {
|
|||||||
xmkdir(COREDIR "/props", 0755);
|
xmkdir(COREDIR "/props", 0755);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link busybox
|
|
||||||
link_busybox();
|
|
||||||
|
|
||||||
// Core only mode
|
// Core only mode
|
||||||
if (access(DISABLEFILE, F_OK) == 0)
|
if (access(DISABLEFILE, F_OK) == 0)
|
||||||
goto core_only;
|
goto core_only;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/sbin/sh
|
#MAGISK
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
#
|
#
|
||||||
# Magisk Flash Script
|
# Magisk Flash Script
|
||||||
@ -31,10 +31,6 @@ umask 022
|
|||||||
OUTFD=$2
|
OUTFD=$2
|
||||||
ZIP=$3
|
ZIP=$3
|
||||||
|
|
||||||
rm -rf $TMPDIR 2>/dev/null
|
|
||||||
mkdir -p $INSTALLER
|
|
||||||
unzip -o "$ZIP" -d $INSTALLER 2>/dev/null
|
|
||||||
|
|
||||||
if [ ! -d "$COMMONDIR" ]; then
|
if [ ! -d "$COMMONDIR" ]; then
|
||||||
echo "! Unable to extract zip file!"
|
echo "! Unable to extract zip file!"
|
||||||
exit 1
|
exit 1
|
||||||
@ -92,16 +88,14 @@ rm -rf $MAGISKBIN 2>/dev/null
|
|||||||
mkdir -p $MAGISKBIN
|
mkdir -p $MAGISKBIN
|
||||||
cp -af $BINDIR/. $COMMONDIR/. $MAGISKBIN
|
cp -af $BINDIR/. $COMMONDIR/. $MAGISKBIN
|
||||||
cp -af $CHROMEDIR $MAGISKBIN
|
cp -af $CHROMEDIR $MAGISKBIN
|
||||||
# Extract busybox
|
cp -af $TMPDIR/bin/busybox $MAGISKBIN/busybox
|
||||||
[ $ARCH = "arm" -o $ARCH = "arm64" ] && BBPATH=lib/armeabi-v7a || BBPATH=lib/x86
|
|
||||||
unzip -p $INSTALLER/common/magisk.apk $BBPATH/libbusybox.so > $MAGISKBIN/busybox
|
|
||||||
chmod -R 755 $MAGISKBIN
|
chmod -R 755 $MAGISKBIN
|
||||||
|
|
||||||
# addon.d
|
# addon.d
|
||||||
if [ -d /system/addon.d ]; then
|
if [ -d /system/addon.d ]; then
|
||||||
ui_print "- Adding addon.d survival script"
|
ui_print "- Adding addon.d survival script"
|
||||||
mount -o rw,remount /system
|
mount -o rw,remount /system
|
||||||
cp $INSTALLER/addon.d/99-magisk.sh /system/addon.d/99-magisk.sh
|
cp -af $INSTALLER/addon.d/99-magisk.sh /system/addon.d/99-magisk.sh
|
||||||
chmod 755 /system/addon.d/99-magisk.sh
|
chmod 755 /system/addon.d/99-magisk.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -39,11 +39,6 @@ abort_wrap() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ ! -d $MAGISKBIN -o ! -f $MAGISKBIN/magiskboot -o ! -f $MAGISKBIN/util_functions.sh ]; then
|
|
||||||
ui_print_wrap "! Cannot find $MAGISKBIN"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -z $BOOTMODE ] && BOOTMODE=false
|
[ -z $BOOTMODE ] && BOOTMODE=false
|
||||||
|
|
||||||
MAGISKBIN=/data/magisk
|
MAGISKBIN=/data/magisk
|
||||||
@ -52,6 +47,11 @@ CHROMEDIR=$MAGISKBIN/chromeos
|
|||||||
# Default permissions
|
# Default permissions
|
||||||
umask 022
|
umask 022
|
||||||
|
|
||||||
|
if [ ! -f $MAGISKBIN/magiskboot -o ! -f $MAGISKBIN/util_functions.sh ]; then
|
||||||
|
ui_print_wrap "! Cannot find $MAGISKBIN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Load utility functions
|
# Load utility functions
|
||||||
. $MAGISKBIN/util_functions.sh
|
. $MAGISKBIN/util_functions.sh
|
||||||
|
|
||||||
|
@ -13,17 +13,16 @@
|
|||||||
##########################################################################################
|
##########################################################################################
|
||||||
|
|
||||||
BOOTMODE=false
|
BOOTMODE=false
|
||||||
INSTALLER=/tmp/uninstall
|
# This path should work in any cases
|
||||||
|
TMPDIR=/dev/tmp
|
||||||
|
|
||||||
|
INSTALLER=$TMPDIR/install
|
||||||
# Default permissions
|
# Default permissions
|
||||||
umask 022
|
umask 022
|
||||||
|
|
||||||
OUTFD=$2
|
OUTFD=$2
|
||||||
ZIP=$3
|
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
|
if [ ! -f $INSTALLER/util_functions.sh ]; then
|
||||||
echo "! Failed: Unable to extract zip file!"
|
echo "! Failed: Unable to extract zip file!"
|
||||||
exit 1
|
exit 1
|
||||||
@ -68,6 +67,7 @@ if is_mounted /data; then
|
|||||||
mkdir -p $MAGISKBIN
|
mkdir -p $MAGISKBIN
|
||||||
cp -af $BINDIR/. $MAGISKBIN
|
cp -af $BINDIR/. $MAGISKBIN
|
||||||
cp -af $CHROMEDIR $MAGISKBIN
|
cp -af $CHROMEDIR $MAGISKBIN
|
||||||
|
cp -af $TMPDIR/bin/busybox $MAGISKBIN/busybox
|
||||||
cp -af $INSTALLER/util_functions.sh $MAGISKBIN
|
cp -af $INSTALLER/util_functions.sh $MAGISKBIN
|
||||||
chmod -R 755 $MAGISKBIN
|
chmod -R 755 $MAGISKBIN
|
||||||
# Run the acttual uninstallation
|
# Run the acttual uninstallation
|
||||||
|
15
scripts/update_binary.sh
Normal file
15
scripts/update_binary.sh
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# BB_ARM and BB_X86 should be generated in build.py
|
||||||
|
TMPDIR=/dev/tmp
|
||||||
|
INSTALLER=$TMPDIR/install
|
||||||
|
BBDIR=$TMPDIR/bin
|
||||||
|
BBBIN=$BBDIR/busybox
|
||||||
|
rm -rf $TMPDIR 2>/dev/null; mkdir -p $BBDIR; touch $BBBIN; chmod 755 $BBBIN
|
||||||
|
echo $BB_ARM | base64 -d | gzip -d > $BBBIN
|
||||||
|
if ! $BBBIN --install -s $TMPDIR/bin >/dev/null 2>&1; then
|
||||||
|
echo $BB_X86 | base64 -d | gzip -d > $BBBIN
|
||||||
|
$BBBIN --install -s $TMPDIR/bin >/dev/null 2>&1 || exit 1
|
||||||
|
fi
|
||||||
|
export PATH=$BBDIR:$PATH
|
||||||
|
mkdir -p $INSTALLER
|
||||||
|
unzip -o "$3" -d $INSTALLER
|
||||||
|
exec sh $INSTALLER/META-INF/com/google/android/updater-script $@
|
@ -127,11 +127,13 @@ recovery_actions() {
|
|||||||
# Preserve environment varibles
|
# Preserve environment varibles
|
||||||
OLD_PATH=$PATH
|
OLD_PATH=$PATH
|
||||||
OLD_LD_PATH=$LD_LIBRARY_PATH
|
OLD_LD_PATH=$LD_LIBRARY_PATH
|
||||||
|
if [ ! -d $TMPDIR/bin ]; then
|
||||||
# Add busybox to PATH
|
# Add busybox to PATH
|
||||||
mkdir -p $TMPDIR/bin
|
mkdir -p $TMPDIR/bin
|
||||||
ln -s $MAGISKBIN/busybox $TMPDIR/bin/busybox
|
ln -s $MAGISKBIN/busybox $TMPDIR/bin/busybox
|
||||||
$MAGISKBIN/busybox --install -s $TMPDIR/bin
|
$MAGISKBIN/busybox --install -s $TMPDIR/bin
|
||||||
export PATH=$TMPDIR/bin:$PATH
|
export PATH=$TMPDIR/bin:$PATH
|
||||||
|
fi
|
||||||
# Temporarily block out all custom recovery binaries/libs
|
# Temporarily block out all custom recovery binaries/libs
|
||||||
mv /sbin /sbin_tmp
|
mv /sbin /sbin_tmp
|
||||||
# Add all possible library paths
|
# Add all possible library paths
|
||||||
|
Loading…
x
Reference in New Issue
Block a user