blob: 15432ec3fae563457dc182d1552fd109f96526b8 [file] [log] [blame]
From a6d463794be51d5f48af655da22c3acb8f24fd02 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Degros?= <fdegros@chromium.org>
Date: Tue, 17 Nov 2020 14:26:09 +1100
Subject: [PATCH] Link and initialize chrome-icu
Make fuse-zip work with the ChromeOS-specific version of
ICU (chrome-icu) on ChromeOS.
---
Makefile | 4 +-
lib/Makefile | 4 +-
lib/fuseZipData.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 115 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
index e72ce52..1406e73 100644
--- a/Makefile
+++ b/Makefile
@@ -8,9 +8,9 @@ mandir=$(datarootdir)/man
man1dir=$(mandir)/man1
manext=.1
PKG_CONFIG ?= pkg-config
-PC_DEPS = fuse libzip icu-uc icu-i18n
+PC_DEPS = fuse libzip
PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS))
-LIBS := -Llib -lfusezip $(shell $(PKG_CONFIG) --libs $(PC_DEPS))
+LIBS := -Llib -lfusezip -licui18n-chrome -licuuc-chrome $(shell $(PKG_CONFIG) --libs $(PC_DEPS))
COMMON_CXXFLAGS = -Wall -Wextra -Wconversion -Wno-sign-compare -Wlogical-op -Wshadow -pedantic -std=c++17
CXXFLAGS = -g -O0 $(COMMON_CXXFLAGS)
RELEASE_CXXFLAGS = -O2 $(COMMON_CXXFLAGS)
diff --git a/lib/Makefile b/lib/Makefile
index 4e4b23a..8c2d098 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,7 +1,9 @@
DEST=libfusezip.a
PKG_CONFIG ?= pkg-config
-PC_DEPS = fuse libzip icu-uc icu-i18n
+PC_DEPS = fuse libzip
PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS))
+PC_CFLAGS += -I"$(SYSROOT)/usr/include/icu-chrome/common"
+PC_CFLAGS += -I"$(SYSROOT)/usr/include/icu-chrome/i18n"
COMMON_CXXFLAGS = -Wall -Wextra -Wconversion -Wno-sign-compare -Wlogical-op -Wshadow -pedantic -std=c++17
CXXFLAGS = -g -O0 $(COMMON_CXXFLAGS)
RELEASE_CXXFLAGS = -O2 $(COMMON_CXXFLAGS)
diff --git a/lib/fuseZipData.cpp b/lib/fuseZipData.cpp
index 9639a8e..6666d6e 100644
--- a/lib/fuseZipData.cpp
+++ b/lib/fuseZipData.cpp
@@ -25,10 +25,18 @@
#include <stdexcept>
#include <functional>
#include <memory>
+#include <system_error>
+#include <fcntl.h>
#include <syslog.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unicode/putil.h>
+#include <unicode/uclean.h>
#include <unicode/ucnv.h>
#include <unicode/ucsdet.h>
+#include <unicode/udata.h>
#include "fuseZipData.h"
#include "extraField.h"
@@ -60,6 +68,105 @@ static void SetPassword(zip_t *const archive) {
namespace {
+[[noreturn]] void ThrowSystemError(const char *const reason) {
+ throw std::system_error(errno, std::system_category(), reason);
+}
+
+// A scoped file handle.
+class ScopedFile {
+ public:
+ // Closes the file.
+ ~ScopedFile() {
+ if (close(fd_) < 0)
+ perror("Cannot close file");
+ }
+
+ explicit ScopedFile(int fd) : fd_(fd) {}
+
+ // No copy.
+ ScopedFile(const ScopedFile &) = delete;
+ ScopedFile &operator=(const ScopedFile &) = delete;
+
+ private:
+ const int fd_;
+};
+
+// A file mapping to memory.
+class FileMapping {
+ public:
+ // Removes the memory mapping.
+ ~FileMapping() {
+ if (munmap(data_, size_) < 0)
+ perror("Cannot unmap file");
+ }
+
+ // Maps a file to memory in read-only mode.
+ // Throws a runtime_error in case of error.
+ explicit FileMapping(const char *const path) {
+ // Open file in read-only mode.
+ const int fd = open(path, O_RDONLY);
+ if (fd < 0)
+ ThrowSystemError("Cannot open file");
+
+ // Ensure file will be closed.
+ const ScopedFile guard(fd);
+
+ // Get file size.
+ struct stat st;
+ if (fstat(fd, &st) < 0)
+ ThrowSystemError("Cannot fstat file");
+
+ size_ = static_cast<size_t>(st.st_size);
+ if (size_ != st.st_size)
+ throw std::runtime_error("File too big to be memory-mapped");
+
+ // Map file to memory.
+ data_ = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (data_ == MAP_FAILED)
+ ThrowSystemError("Cannot mmap file");
+ }
+
+ // No copy.
+ FileMapping(const FileMapping &) = delete;
+ FileMapping &operator=(const FileMapping &) = delete;
+
+ // Start of the memory mapping.
+ const void *data() const { return data_; }
+
+ private:
+ void *data_;
+ size_t size_;
+};
+
+// Initializes and cleans up the ICU library.
+class IcuGuard {
+ public:
+ // Initializes the ICU library.
+ // Throws an runtime_error in case of error.
+ IcuGuard() {
+ UErrorCode error = U_ZERO_ERROR;
+ udata_setCommonData(mappedDataFile.data(), &error);
+ // Never try to load ICU data from files.
+ udata_setFileAccess(UDATA_ONLY_PACKAGES, &error);
+ u_init(&error);
+ if (U_FAILURE(error)) {
+ std::string msg = "Cannot initialize ICU: ";
+ msg += u_errorName(error);
+ throw std::runtime_error(std::move(msg));
+ }
+ }
+
+ // Cleans up the ICU library.
+ ~IcuGuard() { u_cleanup(); }
+
+ // No copy.
+ IcuGuard(const IcuGuard &) = delete;
+ IcuGuard &operator=(const IcuGuard &) = delete;
+
+ private:
+ const FileMapping mappedDataFile{"/opt/google/chrome/icudtl.dat"};
+};
+
struct Closer {
void operator()(UConverter *const conv) const { ucnv_close(conv); }
void operator()(UCharsetDetector *const csd) const { ucsdet_close(csd); }
@@ -236,6 +343,9 @@ void FuseZipData::build_tree(bool readonly) {
}
}
+ // Initialize ICU library and ensure it will be cleaned up.
+ const IcuGuard guard;
+
// Detect filename encoding.
const std::string encoding = DetectEncoding(allNames);
allNames.clear();
--
2.29.2.576.ga3fc446d84-goog