init the awkward code

This commit is contained in:
Bao Nguyen
2023-02-13 19:32:10 +07:00
commit 27170afcac
5426 changed files with 1244579 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
const TYPE_MAP = {
32768: 'FILE',
16384: 'DIR',
40960: 'SYMBOLIC_LINK',
49152: 'SOCKET',
8192: 'CHARACTER_DEVICE',
24576: 'BLOCK_DEVICE',
4096: 'NAMED_PIPE',
};
export class ArchiveReader{
/**
* archive reader
* @param {WasmModule} wasmModule emscripten module
*/
constructor(wasmModule){
this._wasmModule = wasmModule;
this._runCode = wasmModule.runCode;
this._file = null;
this._passphrase = null;
}
/**
* open archive, needs to closed manually
* @param {File} file
*/
open(file){
if( this._file !== null ){
console.warn('Closing previous file');
this.close();
}
const { promise, resolve, reject } = this._promiseHandles();
this._file = file;
const reader = new FileReader();
reader.onload = () => this._loadFile(reader.result,resolve,reject);
reader.readAsArrayBuffer(file);
return promise;
}
/**
* close archive
*/
close(){
this._runCode.closeArchive(this._archive);
this._wasmModule._free(this._filePtr);
this._file = null;
this._filePtr = null;
this._archive = null;
}
/**
* detect if archive has encrypted data
* @returns {boolean|null} null if could not be determined
*/
hasEncryptedData(){
this._archive = this._runCode.openArchive( this._filePtr, this._fileLength, this._passphrase );
this._runCode.getNextEntry(this._archive);
const status = this._runCode.hasEncryptedEntries(this._archive);
if( status === 0 ){
return false;
} else if( status > 0 ){
return true;
} else {
return null;
}
}
/**
* set passphrase to be used with archive
* @param {*} passphrase
*/
setPassphrase(passphrase){
this._passphrase = passphrase;
}
/**
* get archive entries
* @param {boolean} skipExtraction
* @param {string} except don't skip this entry
*/
*entries(skipExtraction = false, except = null){
this._archive = this._runCode.openArchive( this._filePtr, this._fileLength, this._passphrase );
let entry;
while( true ){
entry = this._runCode.getNextEntry(this._archive);
if( entry === 0 ) break;
const entryData = {
size: this._runCode.getEntrySize(entry),
path: this._runCode.getEntryName(entry),
type: TYPE_MAP[this._runCode.getEntryType(entry)],
ref: entry,
};
if( entryData.type === 'FILE' ){
let fileName = entryData.path.split('/');
entryData.fileName = fileName[fileName.length - 1];
}
if( skipExtraction && except !== entryData.path ){
this._runCode.skipEntry(this._archive);
}else{
const ptr = this._runCode.getFileData(this._archive,entryData.size);
if( ptr < 0 ){
throw new Error(this._runCode.getError(this._archive));
}
entryData.fileData = this._wasmModule.HEAP8.slice(ptr,ptr+entryData.size);
this._wasmModule._free(ptr);
}
yield entryData;
}
}
_loadFile(fileBuffer,resolve,reject){
try{
const array = new Uint8Array(fileBuffer);
this._fileLength = array.length;
this._filePtr = this._runCode.malloc(this._fileLength);
this._wasmModule.HEAP8.set(array, this._filePtr);
resolve();
}catch(error){
reject(error);
}
}
_promiseHandles(){
let resolve = null,reject = null;
const promise = new Promise((_resolve,_reject) => {
resolve = _resolve;
reject = _reject;
});
return { promise, resolve, reject };
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,97 @@
/* eslint-disable no-undef */
import libarchive from './wasm-gen/libarchive.js';
export class WasmModule{
constructor(){
this.preRun = [];
this.postRun = [];
this.totalDependencies = 0;
}
print(...text){
console.log(text);
}
printErr(...text){
console.error(text);
}
initFunctions(){
this.runCode = {
// const char * get_version()
getVersion: this.cwrap('get_version', 'string', []),
// void * archive_open( const void * buffer, size_t buffer_size)
// retuns archive pointer
openArchive: this.cwrap('archive_open', 'number', ['number','number','string']),
// void * get_entry(void * archive)
// return archive entry pointer
getNextEntry: this.cwrap('get_next_entry', 'number', ['number']),
// void * get_filedata( void * archive, size_t bufferSize )
getFileData: this.cwrap('get_filedata', 'number', ['number','number']),
// int archive_read_data_skip(struct archive *_a)
skipEntry: this.cwrap('archive_read_data_skip', 'number', ['number']),
// void archive_close( void * archive )
closeArchive: this.cwrap('archive_close', null, ['number'] ),
// la_int64_t archive_entry_size( struct archive_entry * )
getEntrySize: this.cwrap('archive_entry_size', 'number', ['number']),
// const char * archive_entry_pathname( struct archive_entry * )
getEntryName: this.cwrap('archive_entry_pathname', 'string', ['number']),
// __LA_MODE_T archive_entry_filetype( struct archive_entry * )
/*
#define AE_IFMT ((__LA_MODE_T)0170000)
#define AE_IFREG ((__LA_MODE_T)0100000) // Regular file
#define AE_IFLNK ((__LA_MODE_T)0120000) // Sybolic link
#define AE_IFSOCK ((__LA_MODE_T)0140000) // Socket
#define AE_IFCHR ((__LA_MODE_T)0020000) // Character device
#define AE_IFBLK ((__LA_MODE_T)0060000) // Block device
#define AE_IFDIR ((__LA_MODE_T)0040000) // Directory
#define AE_IFIFO ((__LA_MODE_T)0010000) // Named pipe
*/
getEntryType: this.cwrap('archive_entry_filetype', 'number', ['number']),
// const char * archive_error_string(struct archive *);
getError: this.cwrap('archive_error_string', 'string', ['number']),
/*
* Returns 1 if the archive contains at least one encrypted entry.
* If the archive format not support encryption at all
* ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.
* If for any other reason (e.g. not enough data read so far)
* we cannot say whether there are encrypted entries, then
* ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.
* In general, this function will return values below zero when the
* reader is uncertain or totally incapable of encryption support.
* When this function returns 0 you can be sure that the reader
* supports encryption detection but no encrypted entries have
* been found yet.
*
* NOTE: If the metadata/header of an archive is also encrypted, you
* cannot rely on the number of encrypted entries. That is why this
* function does not return the number of encrypted entries but#
* just shows that there are some.
*/
// __LA_DECL int archive_read_has_encrypted_entries(struct archive *);
entryIsEncrypted: this.cwrap('archive_entry_is_encrypted', 'number', ['number']),
hasEncryptedEntries: this.cwrap('archive_read_has_encrypted_entries', 'number', ['number']),
// __LA_DECL int archive_read_add_passphrase(struct archive *, const char *);
addPassphrase: this.cwrap('archive_read_add_passphrase', 'number', ['number','string']),
//this.stringToUTF(str), //
string: (str) => this.allocate(this.intArrayFromString(str), 'i8', 0),
malloc: this.cwrap('malloc', 'number', ['number']),
free: this.cwrap('free', null, ['number']),
};
//console.log(this.runCode.getVersion());
}
monitorRunDependencies(){}
locateFile(path /* ,prefix */ ){
return `wasm-gen/${path}`;
}
}
export function getWasmModule(cb){
libarchive( new WasmModule() ).then( (module) => {
module.initFunctions();
cb(module);
});
}

View File

@@ -0,0 +1,69 @@
import {ArchiveReader} from './archive-reader';
import {getWasmModule} from './wasm-module';
let reader = null;
let busy = false;
getWasmModule( (wasmModule) => {
reader = new ArchiveReader(wasmModule);
busy = false;
self.postMessage({type: 'READY'});
});
self.onmessage = async ({data: msg}) => {
if( busy ){
self.postMessage({ type: 'BUSY' });
return;
}
let skipExtraction = false;
busy = true;
try{
switch(msg.type){
case 'HELLO': // module will respond READY when it's ready
break;
case 'OPEN':
await reader.open(msg.file);
self.postMessage({ type: 'OPENED' });
break;
case 'LIST_FILES':
skipExtraction = true;
// eslint-disable-next-line no-fallthrough
case 'EXTRACT_FILES':
for( const entry of reader.entries(skipExtraction) ){
self.postMessage({ type: 'ENTRY', entry });
}
self.postMessage({ type: 'END' });
break;
case 'EXTRACT_SINGLE_FILE':
for( const entry of reader.entries(true,msg.target) ){
if( entry.fileData ){
self.postMessage({ type: 'FILE', entry });
}
}
break;
case 'CHECK_ENCRYPTION':
self.postMessage({ type: 'ENCRYPTION_STATUS', status: reader.hasEncryptedData() });
break;
case 'SET_PASSPHRASE':
reader.setPassphrase( msg.passphrase );
self.postMessage({ type: 'PASSPHRASE_STATUS', status: true });
break;
default:
throw new Error('Invalid Command');
}
}catch(err){
self.postMessage({
type: 'ERROR',
error: {
message: err.message,
name: err.name,
stack: err.stack
}
});
}finally{
// eslint-disable-next-line require-atomic-updates
busy = false;
}
};