mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-26 12:27:38 +00:00
Rewrite zip signing
This commit is contained in:
parent
42d14d5ca2
commit
fd4aaab137
@ -14,7 +14,6 @@ import com.topjohnwu.magisk.utils.ZipUtils;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
|
public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
|
||||||
@ -38,47 +37,29 @@ public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... params) {
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
|
||||||
FileInputStream in;
|
|
||||||
FileOutputStream out;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// Create temp file
|
// Create temp file
|
||||||
File temp1 = new File(magiskManager.getCacheDir(), "1.zip");
|
File temp1 = new File(magiskManager.getCacheDir(), "1.zip");
|
||||||
File temp2 = new File(magiskManager.getCacheDir(), "2.zip");
|
File temp2 = new File(magiskManager.getCacheDir(), "2.zip");
|
||||||
if (magiskManager.getCacheDir().mkdirs()) {
|
magiskManager.getCacheDir().mkdirs();
|
||||||
temp1.createNewFile();
|
temp1.createNewFile();
|
||||||
temp2.createNewFile();
|
temp2.createNewFile();
|
||||||
}
|
|
||||||
|
|
||||||
out = new FileOutputStream(temp1);
|
|
||||||
|
|
||||||
// First remove top folder in Github source zip, Uri -> temp1
|
// First remove top folder in Github source zip, Uri -> temp1
|
||||||
ZipUtils.removeTopFolder(activity.getContentResolver().openInputStream(mUri), out);
|
ZipUtils.removeTopFolder(activity.getContentResolver().openInputStream(mUri), temp1);
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
out = new FileOutputStream(temp2);
|
|
||||||
|
|
||||||
// Then sign the zip for the first time, temp1 -> temp2
|
// Then sign the zip for the first time, temp1 -> temp2
|
||||||
ZipUtils.signZip(activity, temp1, out, false);
|
ZipUtils.signZip(activity, temp1, temp2, false);
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
// Adjust the zip to prevent unzip issues, temp2 -> temp2
|
// Adjust the zip to prevent unzip issues, temp2 -> temp1
|
||||||
ZipUtils.adjustZip(temp2);
|
ZipUtils.zipAdjust(temp2.getPath(), temp1.getPath());
|
||||||
|
|
||||||
out = new FileOutputStream(temp1);
|
// Finally, sign the whole zip file again, temp1 -> temp2
|
||||||
|
ZipUtils.signZip(activity, temp1, temp2, true);
|
||||||
|
|
||||||
// Finally, sign the whole zip file again, temp2 -> temp1
|
// Write it back to the downloaded zip, temp2 -> Uri
|
||||||
ZipUtils.signZip(activity, temp2, out, true);
|
FileInputStream in = new FileInputStream(temp2);
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
in = new FileInputStream(temp1);
|
|
||||||
|
|
||||||
// Write it back to the downloaded zip, temp1 -> Uri
|
|
||||||
try (OutputStream target = activity.getContentResolver().openOutputStream(mUri)) {
|
try (OutputStream target = activity.getContentResolver().openOutputStream(mUri)) {
|
||||||
byte[] buffer = new byte[4096];
|
byte[] buffer = new byte[4096];
|
||||||
int length;
|
int length;
|
||||||
|
@ -7,7 +7,6 @@ import android.net.Uri;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.v4.content.FileProvider;
|
import android.support.v4.content.FileProvider;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,58 +3,17 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include "zipadjust.h"
|
#include "zipadjust.h"
|
||||||
|
|
||||||
JNIEXPORT jbyteArray JNICALL
|
|
||||||
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust___3BI(JNIEnv *env, jclass type,
|
|
||||||
jbyteArray jbytes, jint size) {
|
|
||||||
fin = (*env)->GetPrimitiveArrayCritical(env, jbytes, NULL);
|
|
||||||
insize = (size_t) size;
|
|
||||||
|
|
||||||
zipadjust(0);
|
|
||||||
|
|
||||||
(*env)->ReleasePrimitiveArrayCritical(env, jbytes, fin, 0);
|
|
||||||
|
|
||||||
jbyteArray ret = (*env)->NewByteArray(env, outsize);
|
|
||||||
(*env)->SetByteArrayRegion(env, ret, 0, outsize, (const jbyte*) fout);
|
|
||||||
free(fout);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust__Ljava_lang_String_2(JNIEnv *env, jclass type, jstring name) {
|
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust(JNIEnv *env, jclass type, jstring filenameIn_,
|
||||||
const char *filename = (*env)->GetStringUTFChars(env, name, NULL);
|
jstring filenameOut_) {
|
||||||
int fd = open(filename, O_RDONLY);
|
const char *filenameIn = (*env)->GetStringUTFChars(env, filenameIn_, 0);
|
||||||
if (fd < 0)
|
const char *filenameOut = (*env)->GetStringUTFChars(env, filenameOut_, 0);
|
||||||
return;
|
|
||||||
|
|
||||||
// Load the file to memory
|
// TODO
|
||||||
insize = lseek(fd, 0, SEEK_END);
|
zipadjust(filenameIn, filenameOut, 0);
|
||||||
lseek(fd, 0, SEEK_SET);
|
|
||||||
fin = malloc(insize);
|
|
||||||
read(fd, fin, insize);
|
|
||||||
|
|
||||||
zipadjust(0);
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
// Open file for output
|
|
||||||
fd = open(filename, O_WRONLY | O_TRUNC);
|
|
||||||
if (fd < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
(*env)->ReleaseStringUTFChars(env, name, filename);
|
|
||||||
|
|
||||||
// Write back to file
|
|
||||||
lseek(fd, 0, SEEK_SET);
|
|
||||||
write(fd, fout, outsize);
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
free(fin);
|
|
||||||
free(fout);
|
|
||||||
|
|
||||||
|
(*env)->ReleaseStringUTFChars(env, filenameIn_, filenameIn);
|
||||||
|
(*env)->ReleaseStringUTFChars(env, filenameOut_, filenameOut);
|
||||||
}
|
}
|
@ -1,9 +1,14 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include "zipadjust.h"
|
#include "zipadjust.h"
|
||||||
|
|
||||||
size_t insize = 0, outsize = 0, alloc = 0;
|
#ifndef O_BINARY
|
||||||
unsigned char *fin = NULL, *fout = NULL;
|
#define O_BINARY 0
|
||||||
|
#define O_TEXT 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
struct local_header_struct {
|
struct local_header_struct {
|
||||||
@ -81,41 +86,43 @@ static int xerror(char* message) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xseekread(off_t offset, void* buf, size_t bytes) {
|
static int xseekread(int fd, off_t offset, void* buf, size_t bytes) {
|
||||||
memcpy(buf, fin + offset, bytes);
|
if (lseek(fd, offset, SEEK_SET) == (off_t)-1) return xerror("Seek failed");
|
||||||
|
if (read(fd, buf, bytes) != bytes) return xerror("Read failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xseekwrite(off_t offset, const void* buf, size_t bytes) {
|
static int xseekwrite(int fd, off_t offset, void* buf, size_t bytes) {
|
||||||
if (offset + bytes > outsize) outsize = offset + bytes;
|
if (lseek(fd, offset, SEEK_SET) == (off_t)-1) return xerror("Seek failed");
|
||||||
if (outsize > alloc) {
|
if (write(fd, buf, bytes) != bytes) return xerror("Write failed");
|
||||||
fout = realloc(fout, outsize);
|
|
||||||
alloc = outsize;
|
|
||||||
}
|
|
||||||
memcpy(fout + offset, buf, bytes);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xfilecopy(off_t offsetIn, off_t offsetOut, size_t bytes) {
|
static int xfilecopy(int fdIn, int fdOut, off_t offsetIn, off_t offsetOut, size_t bytes) {
|
||||||
unsigned int CHUNK = 256 * 1024;
|
if ((offsetIn != (off_t)-1) && (lseek(fdIn, offsetIn, SEEK_SET) == (off_t)-1)) return xerror("Seek failed");
|
||||||
unsigned char* buf = malloc(CHUNK);
|
if ((offsetOut != (off_t)-1) && (lseek(fdOut, offsetOut, SEEK_SET) == (off_t)-1)) return xerror("Seek failed");
|
||||||
|
|
||||||
|
int CHUNK = 256 * 1024;
|
||||||
|
void* buf = malloc(CHUNK);
|
||||||
if (buf == NULL) return xerror("malloc failed");
|
if (buf == NULL) return xerror("malloc failed");
|
||||||
size_t left = bytes;
|
size_t left = bytes;
|
||||||
while (left > 0) {
|
while (left > 0) {
|
||||||
size_t wanted = (left < CHUNK) ? left : CHUNK;
|
size_t wanted = (left < CHUNK) ? left : CHUNK;
|
||||||
xseekread(offsetIn, buf, wanted);
|
size_t r = read(fdIn, buf, wanted);
|
||||||
xseekwrite(offsetOut, buf, wanted);
|
if (r <= 0) return xerror("Read failed");
|
||||||
offsetIn += wanted;
|
if (write(fdOut, buf, r) != r) return xerror("Write failed");
|
||||||
offsetOut += wanted;
|
left -= r;
|
||||||
left -= wanted;
|
|
||||||
}
|
}
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xdecompress(off_t offsetIn, off_t offsetOut, size_t bytes) {
|
static int xdecompress(int fdIn, int fdOut, off_t offsetIn, off_t offsetOut, size_t bytes) {
|
||||||
unsigned int CHUNK = 256 * 1024;
|
if ((offsetIn != (off_t)-1) && (lseek(fdIn, offsetIn, SEEK_SET) == (off_t)-1)) return xerror("Seek failed");
|
||||||
|
if ((offsetOut != (off_t)-1) && (lseek(fdOut, offsetOut, SEEK_SET) == (off_t)-1)) return xerror("Seek failed");
|
||||||
|
|
||||||
|
int CHUNK = 256 * 1024;
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
unsigned have;
|
unsigned have;
|
||||||
@ -132,12 +139,9 @@ static int xdecompress(off_t offsetIn, off_t offsetOut, size_t bytes) {
|
|||||||
if (ret != Z_OK) return xerror("ret != Z_OK");
|
if (ret != Z_OK) return xerror("ret != Z_OK");
|
||||||
|
|
||||||
do {
|
do {
|
||||||
strm.avail_in = insize - offsetIn;
|
strm.avail_in = read(fdIn, in, CHUNK);
|
||||||
if (strm.avail_in == 0) break;
|
if (strm.avail_in == 0) break;
|
||||||
strm.avail_in = (strm.avail_in > CHUNK) ? CHUNK : strm.avail_in;
|
|
||||||
xseekread(offsetIn, in, strm.avail_in);
|
|
||||||
strm.next_in = in;
|
strm.next_in = in;
|
||||||
offsetIn += strm.avail_in;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
strm.avail_out = CHUNK;
|
strm.avail_out = CHUNK;
|
||||||
@ -155,8 +159,10 @@ static int xdecompress(off_t offsetIn, off_t offsetOut, size_t bytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
have = CHUNK - strm.avail_out;
|
have = CHUNK - strm.avail_out;
|
||||||
xseekwrite(offsetOut, out, have);
|
if (write(fdOut, out, have) != have) {
|
||||||
offsetOut += have;
|
(void)inflateEnd(&strm);
|
||||||
|
return xerror("Write failed");
|
||||||
|
}
|
||||||
} while (strm.avail_out == 0);
|
} while (strm.avail_out == 0);
|
||||||
} while (ret != Z_STREAM_END);
|
} while (ret != Z_STREAM_END);
|
||||||
(void)inflateEnd(&strm);
|
(void)inflateEnd(&strm);
|
||||||
@ -164,9 +170,15 @@ static int xdecompress(off_t offsetIn, off_t offsetOut, size_t bytes) {
|
|||||||
return ret == Z_STREAM_END ? 1 : 0;
|
return ret == Z_STREAM_END ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int zipadjust(int decompress) {
|
int zipadjust(const char* filenameIn, const char* filenameOut, int decompress) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
|
|
||||||
|
int fin = open(filenameIn, O_RDONLY | O_BINARY);
|
||||||
|
if (fin > 0) {
|
||||||
|
unsigned int size = lseek(fin, 0, SEEK_END);
|
||||||
|
lseek(fin, 0, SEEK_SET);
|
||||||
|
LOGD("%d bytes\n", size);
|
||||||
|
|
||||||
char filename[1024];
|
char filename[1024];
|
||||||
|
|
||||||
central_footer_t central_footer;
|
central_footer_t central_footer;
|
||||||
@ -175,18 +187,18 @@ int zipadjust(int decompress) {
|
|||||||
uint32_t central_directory_out_size = 0;
|
uint32_t central_directory_out_size = 0;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = insize - 4; i >= 0; i--) {
|
for (i = size - 4; i >= 0; i--) {
|
||||||
uint32_t magic = 0;
|
uint32_t magic = 0;
|
||||||
if (!xseekread(i, &magic, sizeof(uint32_t))) return 0;
|
if (!xseekread(fin, i, &magic, sizeof(uint32_t))) return 0;
|
||||||
if (magic == MAGIC_CENTRAL_FOOTER) {
|
if (magic == MAGIC_CENTRAL_FOOTER) {
|
||||||
LOGD("central footer @ %08X\n", i);
|
LOGD("central footer @ %08X\n", i);
|
||||||
if (!xseekread(i, ¢ral_footer, sizeof(central_footer_t))) return 0;
|
if (!xseekread(fin, i, ¢ral_footer, sizeof(central_footer_t))) return 0;
|
||||||
|
|
||||||
central_header_t central_header;
|
central_header_t central_header;
|
||||||
if (!xseekread(central_footer.central_directory_offset, ¢ral_header, sizeof(central_header_t))) return 0;
|
if (!xseekread(fin, central_footer.central_directory_offset, ¢ral_header, sizeof(central_header_t))) return 0;
|
||||||
if ( central_header.signature == MAGIC_CENTRAL_HEADER ) {
|
if ( central_header.signature == MAGIC_CENTRAL_HEADER ) {
|
||||||
central_directory_in_position = central_footer.central_directory_offset;
|
central_directory_in_position = central_footer.central_directory_offset;
|
||||||
central_directory_in_size = insize - central_footer.central_directory_offset;
|
central_directory_in_size = size - central_footer.central_directory_offset;
|
||||||
LOGD("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size);
|
LOGD("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -197,13 +209,12 @@ int zipadjust(int decompress) {
|
|||||||
|
|
||||||
unsigned char* central_directory_in = (unsigned char*)malloc(central_directory_in_size);
|
unsigned char* central_directory_in = (unsigned char*)malloc(central_directory_in_size);
|
||||||
unsigned char* central_directory_out = (unsigned char*)malloc(central_directory_in_size);
|
unsigned char* central_directory_out = (unsigned char*)malloc(central_directory_in_size);
|
||||||
if (!xseekread(central_directory_in_position, central_directory_in, central_directory_in_size)) return 0;
|
if (!xseekread(fin, central_directory_in_position, central_directory_in, central_directory_in_size)) return 0;
|
||||||
memset(central_directory_out, 0, central_directory_in_size);
|
memset(central_directory_out, 0, central_directory_in_size);
|
||||||
|
|
||||||
|
unlink(filenameOut);
|
||||||
|
int fout = open(filenameOut, O_CREAT | O_WRONLY | O_BINARY, 0644);
|
||||||
fout = (unsigned char*) malloc(insize);
|
if (fout > 0) {
|
||||||
alloc = insize;
|
|
||||||
|
|
||||||
uintptr_t central_directory_in_index = 0;
|
uintptr_t central_directory_in_index = 0;
|
||||||
uintptr_t central_directory_out_index = 0;
|
uintptr_t central_directory_out_index = 0;
|
||||||
@ -221,7 +232,7 @@ int zipadjust(int decompress) {
|
|||||||
LOGD("%s (%d --> %d) [%08X] (%d)\n", filename, central_header->size_uncompressed, central_header->size_compressed, central_header->crc32, central_header->length_extra + central_header->length_comment);
|
LOGD("%s (%d --> %d) [%08X] (%d)\n", filename, central_header->size_uncompressed, central_header->size_compressed, central_header->crc32, central_header->length_extra + central_header->length_comment);
|
||||||
|
|
||||||
local_header_t local_header;
|
local_header_t local_header;
|
||||||
if (!xseekread(central_header->offset, &local_header, sizeof(local_header_t))) return 0;
|
if (!xseekread(fin, central_header->offset, &local_header, sizeof(local_header_t))) return 0;
|
||||||
|
|
||||||
// save and update to next index before we clobber the data
|
// save and update to next index before we clobber the data
|
||||||
uint16_t compression_method_old = central_header->compression_method;
|
uint16_t compression_method_old = central_header->compression_method;
|
||||||
@ -246,15 +257,15 @@ int zipadjust(int decompress) {
|
|||||||
local_header.size_compressed = central_header->size_compressed;
|
local_header.size_compressed = central_header->size_compressed;
|
||||||
local_header.length_extra = 0;
|
local_header.length_extra = 0;
|
||||||
|
|
||||||
if (!xseekwrite(out_index, &local_header, sizeof(local_header_t))) return 0;
|
if (!xseekwrite(fout, out_index, &local_header, sizeof(local_header_t))) return 0;
|
||||||
out_index += sizeof(local_header_t);
|
out_index += sizeof(local_header_t);
|
||||||
if (!xseekwrite(out_index, &filename[0], central_header->length_filename)) return 0;
|
if (!xseekwrite(fout, out_index, &filename[0], central_header->length_filename)) return 0;
|
||||||
out_index += central_header->length_filename;
|
out_index += central_header->length_filename;
|
||||||
|
|
||||||
if (decompress && (compression_method_old == 8)) {
|
if (decompress && (compression_method_old == 8)) {
|
||||||
if (!xdecompress(offset_old + sizeof(local_header_t) + central_header->length_filename + length_extra_old, out_index, size_compressed_old)) return 0;
|
if (!xdecompress(fin, fout, offset_old + sizeof(local_header_t) + central_header->length_filename + length_extra_old, out_index, size_compressed_old)) return 0;
|
||||||
} else {
|
} else {
|
||||||
if (!xfilecopy(offset_old + sizeof(local_header_t) + central_header->length_filename + length_extra_old, out_index, size_compressed_old)) return 0;
|
if (!xfilecopy(fin, fout, offset_old + sizeof(local_header_t) + central_header->length_filename + length_extra_old, out_index, size_compressed_old)) return 0;
|
||||||
}
|
}
|
||||||
out_index += local_header.size_compressed;
|
out_index += local_header.size_compressed;
|
||||||
|
|
||||||
@ -266,16 +277,21 @@ int zipadjust(int decompress) {
|
|||||||
central_footer.central_directory_size = central_directory_out_size;
|
central_footer.central_directory_size = central_directory_out_size;
|
||||||
central_footer.central_directory_offset = out_index;
|
central_footer.central_directory_offset = out_index;
|
||||||
central_footer.length_comment = 0;
|
central_footer.length_comment = 0;
|
||||||
if (!xseekwrite(out_index, central_directory_out, central_directory_out_size)) return 0;
|
if (!xseekwrite(fout, out_index, central_directory_out, central_directory_out_size)) return 0;
|
||||||
out_index += central_directory_out_size;
|
out_index += central_directory_out_size;
|
||||||
if (!xseekwrite(out_index, ¢ral_footer, sizeof(central_footer_t))) return 0;
|
if (!xseekwrite(fout, out_index, ¢ral_footer, sizeof(central_footer_t))) return 0;
|
||||||
|
|
||||||
LOGD("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size);
|
LOGD("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size);
|
||||||
LOGD("central footer @ %08X\n", out_index);
|
LOGD("central footer @ %08X\n", out_index);
|
||||||
|
|
||||||
|
close(fout);
|
||||||
ok = 1;
|
ok = 1;
|
||||||
|
}
|
||||||
|
|
||||||
free(central_directory_in);
|
free(central_directory_in);
|
||||||
free(central_directory_out);
|
free(central_directory_out);
|
||||||
|
close(fin);
|
||||||
|
}
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
@ -3,10 +3,7 @@
|
|||||||
|
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
int zipadjust(int decompress);
|
int zipadjust(const char* filenameIn, const char* filenameOut, int decompress);
|
||||||
|
|
||||||
extern size_t insize, outsize, alloc;
|
|
||||||
extern unsigned char *fin, *fout;
|
|
||||||
|
|
||||||
#define LOG_TAG "zipadjust"
|
#define LOG_TAG "zipadjust"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user