1
0
mirror of https://github.com/SerenityOS/serenity synced 2024-07-09 11:20:46 +00:00

Ladybird/Android: Revive extract_tar_archive function

This function used to live in AndroidPlatform.cpp, but was removed
during the transition to the new app. We still need to extract the
assets from the tarball that CMake creates. At least, until we come
up with a generic "Resource" concept in LibCore.
This commit is contained in:
Andrew Kaster 2023-09-14 12:15:28 -06:00 committed by Andrew Kaster
parent a58ee0ecd2
commit 3c39579510
2 changed files with 157 additions and 2 deletions

View File

@ -5,14 +5,24 @@
*/
#include "ALooperEventLoopImplementation.h"
#include <AK/DeprecatedString.h>
#include <AK/Format.h>
#include <AK/HashMap.h>
#include <AK/LexicalPath.h>
#include <AK/OwnPtr.h>
#include <Ladybird/Utilities.h>
#include <LibArchive/TarStream.h>
#include <LibCore/DirIterator.h>
#include <LibCore/Directory.h>
#include <LibCore/EventLoop.h>
#include <LibCore/System.h>
#include <LibCore/Timer.h>
#include <LibFileSystem/FileSystem.h>
#include <jni.h>
OwnPtr<Core::EventLoop> s_main_event_loop;
static ErrorOr<void> extract_tar_archive(String archive_file, DeprecatedString output_directory);
static OwnPtr<Core::EventLoop> s_main_event_loop;
extern "C" JNIEXPORT void JNICALL
Java_org_serenityos_ladybird_LadybirdActivity_initNativeCode(JNIEnv* env, jobject /* thiz */, jstring resource_dir, jstring tag_name, jobject timer_service)
@ -27,6 +37,15 @@ Java_org_serenityos_ladybird_LadybirdActivity_initNativeCode(JNIEnv* env, jobjec
dbgln("Set resource dir to {}", s_serenity_resource_root);
auto file_or_error = Core::System::open(MUST(String::formatted("{}/res/icons/16x16/app-browser.png", s_serenity_resource_root)), O_RDONLY);
if (file_or_error.is_error()) {
dbgln("No resource files, extracting assets...");
MUST(extract_tar_archive(MUST(String::formatted("{}/ladybird-assets.tar", s_serenity_resource_root)), s_serenity_resource_root));
} else {
dbgln("Found app-browser.png, not re-extracting assets.");
dbgln("Hopefully no developer changed the asset files and expected them to be re-extracted!");
}
jobject timer_service_ref = env->NewGlobalRef(timer_service);
JavaVM* vm = nullptr;
jint ret = env->GetJavaVM(&vm);
@ -40,3 +59,139 @@ Java_org_serenityos_ladybird_LadybirdActivity_execMainEventLoop(JNIEnv*, jobject
{
s_main_event_loop->pump(Core::EventLoop::WaitMode::PollForEvents);
}
ErrorOr<void> extract_tar_archive(String archive_file, DeprecatedString output_directory)
{
constexpr size_t buffer_size = 4096;
auto file = TRY(Core::InputBufferedFile::create(TRY(Core::File::open(archive_file, Core::File::OpenMode::Read))));
DeprecatedString old_pwd = TRY(Core::System::getcwd());
TRY(Core::System::chdir(output_directory));
ScopeGuard go_back = [&old_pwd] { MUST(Core::System::chdir(old_pwd)); };
auto tar_stream = TRY(Archive::TarInputStream::construct(move(file)));
HashMap<DeprecatedString, DeprecatedString> global_overrides;
HashMap<DeprecatedString, DeprecatedString> local_overrides;
auto get_override = [&](StringView key) -> Optional<DeprecatedString> {
Optional<DeprecatedString> maybe_local = local_overrides.get(key);
if (maybe_local.has_value())
return maybe_local;
Optional<DeprecatedString> maybe_global = global_overrides.get(key);
if (maybe_global.has_value())
return maybe_global;
return {};
};
while (!tar_stream->finished()) {
Archive::TarFileHeader const& header = tar_stream->header();
// Handle meta-entries earlier to avoid consuming the file content stream.
if (header.content_is_like_extended_header()) {
switch (header.type_flag()) {
case Archive::TarFileType::GlobalExtendedHeader: {
TRY(tar_stream->for_each_extended_header([&](StringView key, StringView value) {
if (value.length() == 0)
global_overrides.remove(key);
else
global_overrides.set(key, value);
}));
break;
}
case Archive::TarFileType::ExtendedHeader: {
TRY(tar_stream->for_each_extended_header([&](StringView key, StringView value) {
local_overrides.set(key, value);
}));
break;
}
default:
warnln("Unknown extended header type '{}' of {}", (char)header.type_flag(), header.filename());
VERIFY_NOT_REACHED();
}
TRY(tar_stream->advance());
continue;
}
Archive::TarFileStream file_stream = tar_stream->file_contents();
// Handle other header types that don't just have an effect on extraction.
switch (header.type_flag()) {
case Archive::TarFileType::LongName: {
StringBuilder long_name;
Array<u8, buffer_size> buffer;
while (!file_stream.is_eof()) {
auto slice = TRY(file_stream.read_some(buffer));
long_name.append(reinterpret_cast<char*>(slice.data()), slice.size());
}
local_overrides.set("path", long_name.to_deprecated_string());
TRY(tar_stream->advance());
continue;
}
default:
// None of the relevant headers, so continue as normal.
break;
}
LexicalPath path = LexicalPath(header.filename());
if (!header.prefix().is_empty())
path = path.prepend(header.prefix());
DeprecatedString filename = get_override("path"sv).value_or(path.string());
DeprecatedString absolute_path = TRY(FileSystem::absolute_path(filename)).to_deprecated_string();
auto parent_path = LexicalPath(absolute_path).parent();
auto header_mode = TRY(header.mode());
switch (header.type_flag()) {
case Archive::TarFileType::NormalFile:
case Archive::TarFileType::AlternateNormalFile: {
MUST(Core::Directory::create(parent_path, Core::Directory::CreateDirectories::Yes));
int fd = TRY(Core::System::open(absolute_path, O_CREAT | O_WRONLY, header_mode));
Array<u8, buffer_size> buffer;
while (!file_stream.is_eof()) {
auto slice = TRY(file_stream.read_some(buffer));
TRY(Core::System::write(fd, slice));
}
TRY(Core::System::close(fd));
break;
}
case Archive::TarFileType::SymLink: {
MUST(Core::Directory::create(parent_path, Core::Directory::CreateDirectories::Yes));
TRY(Core::System::symlink(header.link_name(), absolute_path));
break;
}
case Archive::TarFileType::Directory: {
MUST(Core::Directory::create(parent_path, Core::Directory::CreateDirectories::Yes));
auto result_or_error = Core::System::mkdir(absolute_path, header_mode);
if (result_or_error.is_error() && result_or_error.error().code() != EEXIST)
return result_or_error.release_error();
break;
}
default:
// FIXME: Implement other file types
warnln("file type '{}' of {} is not yet supported", (char)header.type_flag(), header.filename());
VERIFY_NOT_REACHED();
}
// Non-global headers should be cleared after every file.
local_overrides.clear();
TRY(tar_stream->advance());
}
return {};
}

View File

@ -151,7 +151,7 @@ elseif(ANDROID)
Android/src/main/cpp/ALooperEventLoopImplementation.cpp
Android/src/main/cpp/TimerExecutorService.cpp
)
target_link_libraries(ladybird PRIVATE log jnigraphics android)
target_link_libraries(ladybird PRIVATE LibArchive log jnigraphics android)
else()
# TODO: Check for other GUI frameworks here when we move them in-tree
# For now, we can export a static library of common files for chromes to link to