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
23f697d6
Commit
23f697d6
authored
Jun 25, 2018
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix non-root boot patching
parent
4ff39f88
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
84 additions
and
105 deletions
+84
-105
InstallMagisk.java
src/full/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java
+79
-103
ZipUtils.java
src/full/java/com/topjohnwu/magisk/utils/ZipUtils.java
+5
-2
No files found.
src/full/java/com/topjohnwu/magisk/asyncs/InstallMagisk.java
View file @
23f697d6
...
@@ -17,7 +17,6 @@ import com.topjohnwu.magisk.utils.Utils;
...
@@ -17,7 +17,6 @@ import com.topjohnwu.magisk.utils.Utils;
import
com.topjohnwu.magisk.utils.ZipUtils
;
import
com.topjohnwu.magisk.utils.ZipUtils
;
import
com.topjohnwu.superuser.Shell
;
import
com.topjohnwu.superuser.Shell
;
import
com.topjohnwu.superuser.ShellUtils
;
import
com.topjohnwu.superuser.ShellUtils
;
import
com.topjohnwu.superuser.io.SuFile
;
import
com.topjohnwu.superuser.io.SuFileInputStream
;
import
com.topjohnwu.superuser.io.SuFileInputStream
;
import
com.topjohnwu.utils.SignBoot
;
import
com.topjohnwu.utils.SignBoot
;
...
@@ -43,17 +42,18 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
...
@@ -43,17 +42,18 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
private
static
final
int
DIRECT_MODE
=
1
;
private
static
final
int
DIRECT_MODE
=
1
;
private
static
final
int
FIX_ENV_MODE
=
2
;
private
static
final
int
FIX_ENV_MODE
=
2
;
private
Uri
mBootImg
,
mZip
;
private
Uri
bootUri
,
mZip
;
private
List
<
String
>
console
,
logs
;
private
List
<
String
>
console
,
logs
;
private
String
mBoot
Location
;
private
String
mBoot
;
private
int
mode
;
private
int
mode
;
private
File
install
;
private
File
install
Dir
;
private
ProgressDialog
dialog
;
private
ProgressDialog
dialog
;
private
boolean
highCompression
;
private
MagiskManager
mm
;
public
InstallMagisk
(
Activity
context
,
Uri
zip
)
{
public
InstallMagisk
(
Activity
context
,
Uri
zip
)
{
super
(
context
);
super
(
context
);
mZip
=
zip
;
mZip
=
zip
;
mm
=
MagiskManager
.
get
();
mode
=
FIX_ENV_MODE
;
mode
=
FIX_ENV_MODE
;
}
}
...
@@ -65,14 +65,13 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
...
@@ -65,14 +65,13 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
public
InstallMagisk
(
FlashActivity
context
,
List
<
String
>
console
,
List
<
String
>
logs
,
Uri
zip
,
Uri
boot
)
{
public
InstallMagisk
(
FlashActivity
context
,
List
<
String
>
console
,
List
<
String
>
logs
,
Uri
zip
,
Uri
boot
)
{
this
(
context
,
console
,
logs
,
zip
);
this
(
context
,
console
,
logs
,
zip
);
mBootImg
=
boot
;
bootUri
=
boot
;
highCompression
=
false
;
mode
=
PATCH_MODE
;
mode
=
PATCH_MODE
;
}
}
public
InstallMagisk
(
FlashActivity
context
,
List
<
String
>
console
,
List
<
String
>
logs
,
Uri
zip
,
String
boot
)
{
public
InstallMagisk
(
FlashActivity
context
,
List
<
String
>
console
,
List
<
String
>
logs
,
Uri
zip
,
String
boot
)
{
this
(
context
,
console
,
logs
,
zip
);
this
(
context
,
console
,
logs
,
zip
);
mBoot
Location
=
boot
;
mBoot
=
boot
;
mode
=
DIRECT_MODE
;
mode
=
DIRECT_MODE
;
}
}
...
@@ -86,19 +85,18 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
...
@@ -86,19 +85,18 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
}
}
private
void
extractFiles
(
String
arch
)
throws
IOException
{
private
void
extractFiles
(
String
arch
)
throws
IOException
{
MagiskManager
mm
=
MagiskManager
.
get
();
console
.
add
(
"- Extracting files"
);
console
.
add
(
"- Extracting files"
);
try
(
InputStream
in
=
mm
.
getContentResolver
().
openInputStream
(
mZip
))
{
try
(
InputStream
in
=
mm
.
getContentResolver
().
openInputStream
(
mZip
))
{
if
(
in
==
null
)
throw
new
FileNotFoundException
();
if
(
in
==
null
)
throw
new
FileNotFoundException
();
BufferedInputStream
buf
=
new
BufferedInputStream
(
in
);
BufferedInputStream
buf
=
new
BufferedInputStream
(
in
);
buf
.
mark
(
Integer
.
MAX_VALUE
);
buf
.
mark
(
Integer
.
MAX_VALUE
);
ZipUtils
.
unzip
(
buf
,
install
,
arch
+
"/"
,
true
);
ZipUtils
.
unzip
(
buf
,
install
Dir
,
arch
+
"/"
,
true
);
buf
.
reset
();
buf
.
reset
();
ZipUtils
.
unzip
(
buf
,
install
,
"common/"
,
true
);
ZipUtils
.
unzip
(
buf
,
install
Dir
,
"common/"
,
true
);
buf
.
reset
();
buf
.
reset
();
ZipUtils
.
unzip
(
buf
,
install
,
"chromeos/"
,
false
);
ZipUtils
.
unzip
(
buf
,
install
Dir
,
"chromeos/"
,
false
);
buf
.
reset
();
buf
.
reset
();
ZipUtils
.
unzip
(
buf
,
install
,
"META-INF/com/google/android/update-binary"
,
true
);
ZipUtils
.
unzip
(
buf
,
install
Dir
,
"META-INF/com/google/android/update-binary"
,
true
);
buf
.
close
();
buf
.
close
();
}
catch
(
FileNotFoundException
e
)
{
}
catch
(
FileNotFoundException
e
)
{
console
.
add
(
"! Invalid Uri"
);
console
.
add
(
"! Invalid Uri"
);
...
@@ -108,68 +106,46 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
...
@@ -108,68 +106,46 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
throw
e
;
throw
e
;
}
}
Shell
.
Sync
.
sh
(
Utils
.
fmt
(
"chmod -R 755 %s/*; %s/magiskinit -x magisk %s/magisk"
,
Shell
.
Sync
.
sh
(
Utils
.
fmt
(
"chmod -R 755 %s/*; %s/magiskinit -x magisk %s/magisk"
,
install
,
install
,
install
));
install
Dir
,
installDir
,
installDir
));
}
}
private
boolean
dumpBoot
(
File
boot
)
throws
IOException
{
private
boolean
dumpBoot
()
{
MagiskManager
mm
=
MagiskManager
.
get
();
console
.
add
(
"- Copying boot image to "
+
mBoot
);
switch
(
mode
)
{
// Copy boot image to local
case
PATCH_MODE:
try
(
InputStream
in
=
mm
.
getContentResolver
().
openInputStream
(
bootUri
);
// Copy boot image to local
OutputStream
out
=
new
FileOutputStream
(
mBoot
)
try
(
InputStream
in
=
mm
.
getContentResolver
().
openInputStream
(
mBootImg
);
)
{
OutputStream
out
=
new
FileOutputStream
(
boot
)
if
(
in
==
null
)
)
{
throw
new
FileNotFoundException
();
InputStream
source
;
if
(
in
==
null
)
throw
new
FileNotFoundException
();
InputStream
src
;
if
(
Utils
.
getNameFromUri
(
mm
,
bootUri
).
endsWith
(
".tar"
))
{
if
(
Utils
.
getNameFromUri
(
mm
,
mBootImg
).
endsWith
(
".tar"
))
{
// Extract boot.img from tar
// Extract boot.img from tar
TarInputStream
tar
=
new
TarInputStream
(
new
BufferedInputStream
(
in
));
TarInputStream
tar
=
new
TarInputStream
(
new
BufferedInputStream
(
in
));
org
.
kamranzafar
.
jtar
.
TarEntry
entry
;
org
.
kamranzafar
.
jtar
.
TarEntry
entry
;
while
((
entry
=
tar
.
getNextEntry
())
!=
null
)
{
while
((
entry
=
tar
.
getNextEntry
())
!=
null
)
{
if
(
entry
.
getName
().
equals
(
"boot.img"
))
if
(
entry
.
getName
().
equals
(
"boot.img"
))
break
;
break
;
}
source
=
tar
;
}
else
{
// Direct copy raw image
source
=
new
BufferedInputStream
(
in
);
}
ShellUtils
.
pump
(
source
,
out
);
}
catch
(
FileNotFoundException
e
)
{
console
.
add
(
"! Invalid Uri"
);
throw
e
;
}
catch
(
IOException
e
)
{
console
.
add
(
"! Copy failed"
);
throw
e
;
}
break
;
case
DIRECT_MODE:
console
.
add
(
"- Patch boot/ramdisk image: "
+
mBootLocation
);
if
(
mm
.
remoteMagiskVersionCode
>=
1463
)
{
highCompression
=
Integer
.
parseInt
(
ShellUtils
.
fastCmd
(
Utils
.
fmt
(
"%s/magiskboot --parse %s; echo $?"
,
install
,
mBootLocation
)))
==
2
;
if
(
highCompression
)
console
.
add
(
"! Insufficient boot partition size detected"
);
}
if
(
boot
.
createNewFile
())
{
Shell
.
Sync
.
su
(
"cat "
+
mBootLocation
+
" > "
+
boot
);
}
else
{
console
.
add
(
"! Dump boot image failed"
);
return
false
;
}
}
break
;
src
=
tar
;
default
:
}
else
{
return
false
;
// Direct copy raw image
src
=
new
BufferedInputStream
(
in
);
}
ShellUtils
.
pump
(
src
,
out
);
}
catch
(
FileNotFoundException
e
)
{
console
.
add
(
"! Invalid Uri"
);
return
false
;
}
catch
(
IOException
e
)
{
console
.
add
(
"! Copy failed"
);
return
false
;
}
}
return
true
;
return
true
;
}
}
private
boolean
patchBoot
(
File
boot
,
SuFile
patched_boot
)
throws
IOException
{
private
File
patchBoot
()
throws
IOException
{
MagiskManager
mm
=
MagiskManager
.
get
();
boolean
isSigned
;
boolean
isSigned
;
try
(
InputStream
in
=
new
FileInputStream
(
b
oot
))
{
try
(
InputStream
in
=
new
SuFileInputStream
(
mB
oot
))
{
isSigned
=
SignBoot
.
verifySignature
(
in
,
null
);
isSigned
=
SignBoot
.
verifySignature
(
in
,
null
);
if
(
isSigned
)
{
if
(
isSigned
)
{
console
.
add
(
"- Boot image is signed with AVB 1.0"
);
console
.
add
(
"- Boot image is signed with AVB 1.0"
);
...
@@ -181,35 +157,33 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
...
@@ -181,35 +157,33 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
// Patch boot image
// Patch boot image
Shell
.
Sync
.
sh
(
console
,
logs
,
Shell
.
Sync
.
sh
(
console
,
logs
,
"cd "
+
install
,
"cd "
+
install
Dir
,
Utils
.
fmt
(
"KEEPFORCEENCRYPT=%b KEEPVERITY=%b
HIGHCOMP=%b
"
+
Utils
.
fmt
(
"KEEPFORCEENCRYPT=%b KEEPVERITY=%b
sh update-binary indep
"
+
"
sh update-binary indep
boot_patch.sh %s || echo 'Failed!'"
,
"boot_patch.sh %s || echo 'Failed!'"
,
mm
.
keepEnc
,
mm
.
keepVerity
,
highCompression
,
b
oot
));
mm
.
keepEnc
,
mm
.
keepVerity
,
mB
oot
));
if
(
TextUtils
.
equals
(
console
.
get
(
console
.
size
()
-
1
),
"Failed!"
))
if
(
TextUtils
.
equals
(
console
.
get
(
console
.
size
()
-
1
),
"Failed!"
))
return
false
;
return
null
;
Shell
.
Sync
.
sh
(
"mv -f new-boot.img ../"
,
Shell
.
Sync
.
sh
(
"mv bin/busybox busybox"
,
"mv bin/busybox busybox"
,
"rm -rf magisk.apk bin boot.img update-binary"
,
"rm -rf magisk.apk bin *.img update-binary"
,
"cd /"
);
"cd /"
);
File
patched
=
new
File
(
installDir
,
"new-boot.img"
);
if
(
isSigned
)
{
if
(
isSigned
)
{
console
.
add
(
"- Signing boot image with test keys"
);
console
.
add
(
"- Signing boot image with test keys"
);
File
signed
=
new
File
(
install
.
getParent
()
,
"signed.img"
);
File
signed
=
new
File
(
install
Dir
,
"signed.img"
);
try
(
InputStream
in
=
new
SuFileInputStream
(
patched_boot
);
try
(
InputStream
in
=
new
BufferedInputStream
(
new
FileInputStream
(
patched
)
);
OutputStream
out
=
new
BufferedOutputStream
(
new
FileOutputStream
(
signed
))
OutputStream
out
=
new
BufferedOutputStream
(
new
FileOutputStream
(
signed
))
)
{
)
{
SignBoot
.
doSignature
(
"/boot"
,
in
,
out
,
null
,
null
);
SignBoot
.
doSignature
(
"/boot"
,
in
,
out
,
null
,
null
);
}
}
Shell
.
Sync
.
sh
(
"mv -f "
+
signed
+
" "
+
patched_boot
);
signed
.
renameTo
(
patched
);
}
}
return
true
;
return
patched
;
}
}
private
void
outputBoot
(
SuFile
patched_boot
)
throws
IOException
{
private
void
outputBoot
(
File
patched
)
throws
IOException
{
MagiskManager
mm
=
MagiskManager
.
get
();
switch
(
mode
)
{
switch
(
mode
)
{
case
PATCH_MODE:
case
PATCH_MODE:
File
dest
=
new
File
(
Const
.
EXTERNAL_PATH
,
"patched_boot"
+
mm
.
bootFormat
);
File
dest
=
new
File
(
Const
.
EXTERNAL_PATH
,
"patched_boot"
+
mm
.
bootFormat
);
...
@@ -218,14 +192,14 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
...
@@ -218,14 +192,14 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
switch
(
mm
.
bootFormat
)
{
switch
(
mm
.
bootFormat
)
{
case
".img.tar"
:
case
".img.tar"
:
out
=
new
TarOutputStream
(
new
BufferedOutputStream
(
new
FileOutputStream
(
dest
)));
out
=
new
TarOutputStream
(
new
BufferedOutputStream
(
new
FileOutputStream
(
dest
)));
((
TarOutputStream
)
out
).
putNextEntry
(
new
TarEntry
(
patched
_boot
,
"boot.img"
));
((
TarOutputStream
)
out
).
putNextEntry
(
new
TarEntry
(
patched
,
"boot.img"
));
break
;
break
;
default
:
default
:
case
".img"
:
case
".img"
:
out
=
new
BufferedOutputStream
(
new
FileOutputStream
(
dest
));
out
=
new
BufferedOutputStream
(
new
FileOutputStream
(
dest
));
break
;
break
;
}
}
try
(
InputStream
in
=
new
SuFileInputStream
(
patched_boot
))
{
try
(
InputStream
in
=
new
FileInputStream
(
patched
))
{
ShellUtils
.
pump
(
in
,
out
);
ShellUtils
.
pump
(
in
,
out
);
out
.
close
();
out
.
close
();
}
}
...
@@ -236,32 +210,37 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
...
@@ -236,32 +210,37 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
console
.
add
(
"*********************************"
);
console
.
add
(
"*********************************"
);
break
;
break
;
case
DIRECT_MODE:
case
DIRECT_MODE:
String
binPath
=
mm
.
remoteMagiskVersionCode
>=
Const
.
MAGISK_VER
.
HIDDEN_PATH
?
"/data/adb/magisk"
:
"/data/magisk"
;
String
binPath
=
mm
.
remoteMagiskVersionCode
>=
Const
.
MAGISK_VER
.
HIDDEN_PATH
?
"/data/adb/magisk"
:
"/data/magisk"
;
Shell
.
Sync
.
su
(
console
,
logs
,
Shell
.
Sync
.
su
(
console
,
logs
,
Utils
.
fmt
(
"flash_boot_image %s %s; rm -f %s"
,
patched
,
mBoot
,
patched
),
Utils
.
fmt
(
"rm -rf %s/*; mkdir -p %s; chmod 700 /data/adb"
,
binPath
,
binPath
),
Utils
.
fmt
(
"rm -rf %s/*; mkdir -p %s; chmod 700 /data/adb"
,
binPath
,
binPath
),
Utils
.
fmt
(
"cp -af %s/* %s; rm -rf %s"
,
install
,
binPath
,
install
),
Utils
.
fmt
(
"cp -af %s/* %s; rm -rf %s"
,
installDir
,
binPath
,
installDir
),
Utils
.
fmt
(
"flash_boot_image %s %s"
,
patched_boot
,
mBootLocation
),
mm
.
keepVerity
?
""
:
"patch_dtbo_image"
);
mm
.
keepVerity
?
""
:
"patch_dtbo_image"
);
break
;
break
;
}
}
patched
_boot
.
delete
();
patched
.
delete
();
}
}
@Override
@Override
protected
Boolean
doInBackground
(
Void
...
voids
)
{
protected
Boolean
doInBackground
(
Void
...
voids
)
{
MagiskManager
mm
=
MagiskManager
.
get
();
if
(
mode
==
FIX_ENV_MODE
)
{
if
(
mode
==
FIX_ENV_MODE
)
{
install
=
new
File
(
"/data/adb/magisk"
);
install
Dir
=
new
File
(
"/data/adb/magisk"
);
Shell
.
Sync
.
sh
(
"rm -rf "
+
install
+
"/*"
);
Shell
.
Sync
.
sh
(
"rm -rf "
+
install
Dir
+
"/*"
);
}
else
{
}
else
{
install
=
new
File
(
install
Dir
=
new
File
(
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
N
?
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
N
?
mm
.
createDeviceProtectedStorageContext
()
:
mm
)
mm
.
createDeviceProtectedStorageContext
()
:
mm
)
.
getFilesDir
().
getParent
()
.
getFilesDir
().
getParent
()
,
"install"
);
,
"install"
);
Shell
.
Sync
.
sh
(
"rm -rf "
+
install
);
Shell
.
Sync
.
sh
(
"rm -rf "
+
installDir
);
install
.
mkdirs
();
installDir
.
mkdirs
();
}
if
(
mode
==
PATCH_MODE
)
{
mBoot
=
new
File
(
installDir
,
"boot.img"
).
getAbsolutePath
();
if
(!
dumpBoot
())
return
false
;
}
}
List
<
String
>
abis
=
Arrays
.
asList
(
Build
.
SUPPORTED_ABIS
);
List
<
String
>
abis
=
Arrays
.
asList
(
Build
.
SUPPORTED_ABIS
);
...
@@ -284,7 +263,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
...
@@ -284,7 +263,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
extractFiles
(
arch
);
extractFiles
(
arch
);
if
(
mode
==
FIX_ENV_MODE
)
{
if
(
mode
==
FIX_ENV_MODE
)
{
Shell
.
Sync
.
sh
(
Shell
.
Sync
.
sh
(
"cd "
+
install
,
"cd "
+
install
Dir
,
"sh update-binary extract"
,
"sh update-binary extract"
,
"rm -f update-binary magisk.apk"
,
"rm -f update-binary magisk.apk"
,
"cd /"
,
"cd /"
,
...
@@ -292,13 +271,10 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
...
@@ -292,13 +271,10 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
"/sbin/.core/mirror/bin/busybox --install -s /sbin/.core/busybox"
"/sbin/.core/mirror/bin/busybox --install -s /sbin/.core/busybox"
);
);
}
else
{
}
else
{
File
boot
=
new
File
(
install
,
"boot.img"
);
File
patched
=
patchBoot
();
SuFile
patched_boot
=
new
SuFile
(
install
.
getParent
(),
"new-boot.img"
);
if
(
patched
==
null
)
if
(!
dumpBoot
(
boot
)
||
!
patchBoot
(
boot
,
patched_boot
))
return
false
;
return
false
;
outputBoot
(
patched_boot
);
outputBoot
(
patched
);
console
.
add
(
"- All done!"
);
console
.
add
(
"- All done!"
);
}
}
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
...
@@ -317,7 +293,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
...
@@ -317,7 +293,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
// Running in FlashActivity
// Running in FlashActivity
FlashActivity
activity
=
(
FlashActivity
)
getActivity
();
FlashActivity
activity
=
(
FlashActivity
)
getActivity
();
if
(!
result
)
{
if
(!
result
)
{
Shell
.
Async
.
sh
(
"rm -rf "
+
install
);
Shell
.
Async
.
sh
(
"rm -rf "
+
install
Dir
);
console
.
add
(
"! Installation failed"
);
console
.
add
(
"! Installation failed"
);
activity
.
reboot
.
setVisibility
(
View
.
GONE
);
activity
.
reboot
.
setVisibility
(
View
.
GONE
);
}
}
...
...
src/full/java/com/topjohnwu/magisk/utils/ZipUtils.java
View file @
23f697d6
...
@@ -40,8 +40,11 @@ public class ZipUtils {
...
@@ -40,8 +40,11 @@ public class ZipUtils {
}
else
{
}
else
{
name
=
entry
.
getName
();
name
=
entry
.
getName
();
}
}
SuFile
dest
=
new
SuFile
(
folder
,
name
);
File
dest
=
new
File
(
folder
,
name
);
dest
.
getParentFile
().
mkdirs
();
if
(!
dest
.
getParentFile
().
mkdirs
())
{
dest
=
new
SuFile
(
folder
,
name
);
dest
.
getParentFile
().
mkdirs
();
}
try
(
OutputStream
out
=
new
SuFileOutputStream
(
dest
))
{
try
(
OutputStream
out
=
new
SuFileOutputStream
(
dest
))
{
ShellUtils
.
pump
(
zipfile
,
out
);
ShellUtils
.
pump
(
zipfile
,
out
);
}
}
...
...
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