// The privileges that required in Filesystem API
const std::string kPrivilegeFilesystemRead = "http://tizen.org/privilege/filesystem.read";
const std::string kPrivilegeFilesystemWrite = "http://tizen.org/privilege/filesystem.write";
+
+std::string GetFopenMode(const picojson::value& args) {
+ ScopeLogger();
+ const std::string& open_mode = args.get("openMode").get<std::string>();
+
+ if ("rw" == open_mode) {
+ return "r+";
+ } else if ("rwo" == open_mode) {
+ return "w+";
+ }
+
+ return open_mode;
+}
+
+bool WriteAccessRequested(const picojson::value& args) {
+ ScopeLogger();
+ const std::string& open_mode = args.get("openMode").get<std::string>();
+
+ return std::string::npos != open_mode.find("w") || "a" == open_mode;
+}
+
+bool ReadAccessRequested(const picojson::value& args) {
+ ScopeLogger();
+ const std::string& open_mode = args.get("openMode").get<std::string>();
+
+ return std::string::npos != open_mode.find("r");
+}
+
+bool ShouldMakeParents(const picojson::value& args) {
+ ScopeLogger();
+ const std::string& path = args.get("path").get<std::string>();
+ struct stat buf {};
+ if (FilesystemUtils::CheckIfExists(FilesystemUtils::Dirname(path), &buf)) {
+ return false;
+ }
+ bool make_parents = args.get("makeParents").get<bool>();
+ const std::string& open_mode = args.get("openMode").get<std::string>();
+ return make_parents && ("w" == open_mode || "a" == open_mode || "rwo" == open_mode);
+}
}
using namespace common;
REGISTER_SYNC("File_copyTo", CopyTo);
REGISTER_SYNC("FileSystemManager_getCanonicalPath", FileSystemManagerGetCanonicalPath);
+ REGISTER_SYNC("FileSystemManager_openFile", FileSystemManagerOpenFile);
REGISTER_SYNC("FileSystemManager_createDirectory", FileSystemManagerCreateDirectory);
REGISTER_SYNC("FileSystemManager_deleteFile", FileSystemManagerDeleteFile);
REGISTER_SYNC("FileSystemManager_deleteDirectory", FileSystemManagerDeleteDirectory);
REGISTER_SYNC("FileSystemManager_pathExists", FileSystemManagerPathExists);
REGISTER_SYNC("FileSystemManager_getLimits", FileSystemManagerGetLimits);
+ REGISTER_SYNC("FileHandle_seek", FileHandleSeek);
+ REGISTER_SYNC("FileHandle_readString", FileHandleReadString);
+ REGISTER_SYNC("FileHandle_writeString", FileHandleWriteString);
+ REGISTER_SYNC("FileHandle_readData", FileHandleReadData);
+ REGISTER_SYNC("FileHandle_writeData", FileHandleWriteData);
+ REGISTER_SYNC("FileHandle_flush", FileHandleFlush);
+ REGISTER_SYNC("FileHandle_sync", FileHandleSync);
+ REGISTER_SYNC("FileHandle_close", FileHandleClose);
+
#undef REGISTER_SYNC
FilesystemManager::GetInstance().AddListener(this);
}
write_file(data, len, file);
}
+#define FIRST_BIT_MASK 0x80
+#define SECOND_BIT_MASK 0x40
+#define THIRD_BIT_MASK 0x20
+#define FOURTH_BIT_MASK 0x10
+#define FIFTH_BIT_MASK 0x08
+
+enum class ByteOfUTF8Classification {
+ NOT_VALID,
+ ONE_BYTE_CHAR,
+ FIRST_OF_2_BYTES,
+ FIRST_OF_3_BYTES,
+ FIRST_OF_4_BYTES,
+ EXTENSION_BYTE
+};
+
+ByteOfUTF8Classification is_utf8_byte(uint8_t byte) {
+ if (FIRST_BIT_MASK & byte) {
+ if (SECOND_BIT_MASK & byte) {
+ if (THIRD_BIT_MASK & byte) {
+ if (FOURTH_BIT_MASK & byte) {
+ if (FIFTH_BIT_MASK & byte) {
+ return ByteOfUTF8Classification::NOT_VALID;
+ } else {
+ return ByteOfUTF8Classification::FIRST_OF_4_BYTES;
+ }
+ } else {
+ return ByteOfUTF8Classification::FIRST_OF_3_BYTES;
+ }
+ } else {
+ return ByteOfUTF8Classification::FIRST_OF_2_BYTES;
+ }
+ } else {
+ return ByteOfUTF8Classification::EXTENSION_BYTE;
+ }
+ } else {
+ return ByteOfUTF8Classification::ONE_BYTE_CHAR;
+ }
+}
+
+void skip_partial_character(std::vector<std::uint8_t>& buf) {
+ auto buf_position = buf.begin();
+
+ while (buf.end() != buf_position &&
+ (ByteOfUTF8Classification::EXTENSION_BYTE == is_utf8_byte(*buf_position))) {
+ buf_position = buf.erase(buf_position);
+ LoggerD("Removed UTF-8 Extension Byte from begining of read buffer");
+ }
+}
+
+bool validate_and_check_character_count(std::vector<std::uint8_t>& buf, unsigned long& char_count,
+ short& no_of_extensions_expected) {
+ ScopeLogger();
+ char_count = 0;
+ no_of_extensions_expected = 0;
+ auto buf_position = buf.begin();
+
+ skip_partial_character(buf);
+
+ while (buf.end() != buf_position) {
+ switch (is_utf8_byte(*buf_position)) {
+ case ByteOfUTF8Classification::EXTENSION_BYTE:
+ no_of_extensions_expected--;
+ if (0 > no_of_extensions_expected) {
+ return false;
+ } else if (0 == no_of_extensions_expected) {
+ char_count++;
+ }
+ break;
+ case ByteOfUTF8Classification::ONE_BYTE_CHAR:
+ if (0 != no_of_extensions_expected) {
+ return false;
+ }
+ char_count++;
+ break;
+ case ByteOfUTF8Classification::FIRST_OF_2_BYTES:
+ if (0 != no_of_extensions_expected) {
+ return false;
+ }
+ no_of_extensions_expected = 1;
+ break;
+ case ByteOfUTF8Classification::FIRST_OF_3_BYTES:
+ if (0 != no_of_extensions_expected) {
+ return false;
+ }
+ no_of_extensions_expected = 2;
+ break;
+ case ByteOfUTF8Classification::FIRST_OF_4_BYTES:
+ if (0 != no_of_extensions_expected) {
+ return false;
+ }
+ no_of_extensions_expected = 3;
+ break;
+ case ByteOfUTF8Classification::NOT_VALID:
+ return false;
+ default:
+ LoggerE("Abnormal value returned from is_utf8_byte function");
+ }
+ buf_position++;
+ }
+ return true;
+}
+
+/*
+ * add_utf8_chars_to_buffer
+ * Method returns false if byte read is not a valid utf8 char
+ */
+bool add_utf8_chars_to_buffer(FILE* file, std::vector<std::uint8_t>& buf, int chars_to_read,
+ short& extension_bytes_expected) {
+ int character;
+ LoggerD("chars_to_read: %i", chars_to_read);
+ LoggerD("extension_bytes_expected: %i", extension_bytes_expected);
+ while (chars_to_read) {
+ if (extension_bytes_expected) {
+ character = getc(file);
+ if (EOF == character) {
+ return false;
+ }
+ buf.push_back(character);
+ if (!--extension_bytes_expected) {
+ chars_to_read--;
+ }
+ continue;
+ }
+ character = getc(file);
+ if(EOF == character) {
+ return false;
+ }
+ buf.push_back(character);
+ switch (is_utf8_byte(character)) {
+ case ByteOfUTF8Classification::ONE_BYTE_CHAR:
+ chars_to_read--;
+ break;
+ case ByteOfUTF8Classification::FIRST_OF_2_BYTES:
+ extension_bytes_expected = 1;
+ break;
+ case ByteOfUTF8Classification::FIRST_OF_3_BYTES:
+ extension_bytes_expected = 2;
+ break;
+ case ByteOfUTF8Classification::FIRST_OF_4_BYTES:
+ extension_bytes_expected = 3;
+ break;
+ case ByteOfUTF8Classification::EXTENSION_BYTE:
+ LoggerD("unexpected EXTENSION_BYTE");
+ return false;
+ case ByteOfUTF8Classification::NOT_VALID:
+ LoggerE("unexpected NOT_VALID byte while reading utf-8");
+ return false;
+ default:
+ LoggerE("Abnormal. Last read char: %c: ", character);
+ return false;
+ }
+ }
+ return true;
+}
+
namespace base64 {
static const char int_to_char[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
FilesystemManager::GetInstance().GetCanonicalPath(path, onSuccess, onError);
}
+
+namespace {
+
+FILE* OpenFile(const std::string& path, const std::string& fopen_mode) {
+ FILE* file = std::fopen(path.c_str(), fopen_mode.c_str());
+ if (!file) {
+ throw std::system_error{errno, std::generic_category(), "Could not open file"};
+ }
+
+ return file;
+}
+
+FILE* MakeParentsAndOpenFile(const std::string& path, const std::string& fopen_mode) {
+ /*
+ * If fopen fails, created parent directories have to be removed.
+ * Save the path to the first nonexistent parent directory in the file path,
+ * to know where to start recursive removal
+ */
+ std::string first_nonexistent_parent = FilesystemUtils::Dirname(path);
+ struct ::stat buf;
+
+ while (!FilesystemUtils::CheckIfExists(FilesystemUtils::Dirname(first_nonexistent_parent),
+ &buf)) {
+ first_nonexistent_parent = FilesystemUtils::Dirname(first_nonexistent_parent);
+ }
+
+ FilesystemUtils::Mkdir(FilesystemUtils::Dirname(path), true);
+
+ FILE* file = std::fopen(path.c_str(), fopen_mode.c_str());
+ if (file) {
+ return file;
+ }
+
+ std::system_error fopen_error = std::system_error(errno, std::generic_category(), "Could not open file");
+
+ try {
+ FilesystemUtils::RemoveDirectoryRecursively(first_nonexistent_parent);
+ } catch (const std::system_error& error) {
+ LoggerD("Failed to remove created parent directories: %s", error.what());
+ }
+
+ throw fopen_error;
+}
+
+} //namespace
+
+void FilesystemInstance::FileSystemManagerOpenFile(const picojson::value& args,
+ picojson::object& out) {
+ ScopeLogger();
+ const int unique_id = static_cast<int>(args.get("id").get<double>());
+ if (opened_files.find(unique_id) != opened_files.end()) {
+ LogAndReportError(IOException("Internal error (id is already in use)"), out);
+ return;
+ }
+
+ if (WriteAccessRequested(args)) {
+ CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
+ }
+
+ if (ReadAccessRequested(args)) {
+ CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
+ }
+
+ const std::string& path = args.get("path").get<std::string>();
+ const std::string open_mode = GetFopenMode(args);
+ FILE* file = nullptr;
+ try {
+ if (ShouldMakeParents(args)) {
+ file = MakeParentsAndOpenFile(path, open_mode);
+ } else {
+ file = OpenFile(path, open_mode);
+ }
+ } catch (const std::system_error& error) {
+ FilesystemUtils::TranslateException(error, out);
+ return;
+ }
+
+ opened_files.emplace(std::make_pair(unique_id, std::make_shared<FileHandle>(file)));
+ ReportSuccess(out);
+}
+
void FilesystemInstance::FileSystemManagerCreateDirectory(const picojson::value& args,
picojson::object& out) {
ScopeLogger();
ReportSuccess(response, out);
}
+void FilesystemInstance::FileHandleSeek(const picojson::value& args, picojson::object& out) {
+ ScopeLogger();
+ const int fh_id = static_cast<int>(args.get("id").get<double>());
+ const long offset = static_cast<long>(args.get("offset").get<double>());
+ const std::string& _whence = args.get("whence").get<std::string>();
+
+ auto fh = opened_files.find(fh_id);
+
+ int whence = SEEK_SET;
+
+ if ("CURRENT" == _whence) {
+ whence = SEEK_CUR;
+ LoggerD("SEEK_CUR selected");
+ } else if ("END" == _whence) {
+ whence = SEEK_END;
+ LoggerD("SEEK_END selected");
+ }
+
+ if (opened_files.end() == fh) {
+ LogAndReportError(IOException("Invalid FileHandle"), out);
+ return;
+ }
+
+ auto handle = fh->second;
+
+ auto logic = [handle, whence, offset](decltype(out) out) {
+ try {
+ long ret = fseek(handle->file_handle, offset, whence);
+ if (0 != ret) {
+ LoggerE("fseek returned failed");
+ int errsv = errno;
+ std::string error_message =
+ std::string("seek failed, fileHandle may be corrupted, error message: ") +
+ strerror(errsv);
+ LogAndReportError(IOException(error_message.c_str()), out);
+ return;
+ }
+
+ ret = ftell(handle->file_handle);
+ if (-1L == ret) {
+ LoggerE("ftell returned failed");
+ int errsv = errno;
+ std::string error_message =
+ std::string("seek failed, fileHandle may be corrupted, error message: ") +
+ strerror(errsv);
+ LogAndReportError(IOException(error_message.c_str()), out);
+ return;
+ }
+
+ ReportSuccess(picojson::value((double)ret), out);
+ } catch (std::runtime_error& e) {
+ LoggerE("Cannot read, cause: %s", e.what());
+ LogAndReportError(IOException(e.what()), out);
+ return;
+ }
+ };
+
+ bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
+
+ if (blocking) {
+ logic(out);
+ return;
+ } else {
+ // Async logic
+ double callback_id = args.get("callbackId").get<double>();
+ this->worker.add_job([this, callback_id, logic] {
+ picojson::value response = picojson::value(picojson::object());
+ picojson::object& async_out = response.get<picojson::object>();
+ async_out["callbackId"] = picojson::value(callback_id);
+ logic(async_out);
+ this->PostMessage(response.serialize().c_str());
+ });
+
+ // Sync return
+ ReportSuccess(out);
+ return;
+ }
+}
+
+void FilesystemInstance::FileHandleReadString(const picojson::value& args, picojson::object& out) {
+ ScopeLogger();
+ CHECK_EXIST(args, "id", out)
+ const int fh_id = static_cast<int>(args.get("id").get<double>());
+ const std::string& encoding =
+ args.contains("encoding") ? args.get("encoding").get<std::string>() : "utf-8";
+
+ auto fh = opened_files.find(fh_id);
+
+ if (opened_files.end() == fh) {
+ LogAndReportError(IOException("Invalid FileHandle"), out);
+ return;
+ }
+
+ size_t count;
+ bool whole_file = false;
+ if (args.contains("count")) {
+ count = static_cast<size_t>(args.get("count").get<double>());
+ } else {
+ try {
+ count = file_size(fh->second->file_handle);
+ whole_file = true;
+ } catch (const std::system_error& e) {
+ LogAndReportError(IOException(e.what()), out);
+ return;
+ }
+ }
+ LoggerD("count: %d", count);
+
+ if (std::string::npos == count) {
+ LogAndReportError(InvalidValuesException("Invalid count was given"), out);
+ return;
+ }
+
+ auto handle = fh->second;
+
+ auto logic = [handle, count, encoding, whole_file](decltype(out) out) {
+ try {
+ std::vector<std::uint8_t> buf = read_file(handle->file_handle, count);
+ if (encoding == "iso-8859-1") { // for iso-8859-1 1 byte is equal to 1 character
+ out["result"] = picojson::value(picojson::string_type, true);
+ latin1::to_utf8(buf, out["result"].get<std::string>());
+ ReportSuccess(out);
+ } else if (encoding == "utf-8") {
+ unsigned long char_count;
+ short expected_extension_bytes;
+ if (!validate_and_check_character_count(buf, char_count, expected_extension_bytes)) {
+ LogAndReportError(
+ IOException("File doesn't contain UTF-8 encoded string with given length"), out);
+ return;
+ }
+ LoggerD("char_count: %ld", char_count);
+ LoggerD("ftell: %i", ftell(handle->file_handle));
+ if (!(std::feof(
+ handle->file_handle))) { // read number of characters if not whole file read
+ LoggerD("count parameter given: %d", count);
+ if (!whole_file && !add_utf8_chars_to_buffer(handle->file_handle, buf, count - char_count,
+ expected_extension_bytes)) {
+ LogAndReportError(
+ IOException("File doesn't contain UTF-8 encoded string with given length"), out);
+ }
+ }
+ buf.push_back('\0');
+ const char* str = (const char*)buf.data();
+ ReportSuccess(picojson::value{str}, out);
+ } else {
+ LogAndReportError(NotSupportedException("Given encoding is not supported."), out);
+ return;
+ }
+ } catch (std::runtime_error& e) {
+ LoggerE("Cannot read, cause: %s", e.what());
+ LogAndReportError(IOException(e.what()), out);
+ return;
+ }
+ };
+
+ bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
+
+ if (blocking) {
+ logic(out);
+ return;
+ } else {
+ // Async logic
+ double callback_id = args.get("callbackId").get<double>();
+ this->worker.add_job([this, callback_id, logic] {
+ picojson::value response = picojson::value(picojson::object());
+ picojson::object& async_out = response.get<picojson::object>();
+ async_out["callbackId"] = picojson::value(callback_id);
+ logic(async_out);
+ this->PostMessage(response.serialize().c_str());
+ });
+
+ // Sync return
+ ReportSuccess(out);
+ return;
+ }
+}
+
+void FilesystemInstance::FileHandleWriteString(const picojson::value& args, picojson::object& out) {
+ ScopeLogger();
+ CHECK_EXIST(args, "id", out)
+ CHECK_EXIST(args, "string", out)
+ const int fh_id = static_cast<int>(args.get("id").get<double>());
+ const std::string& str = args.get("string").get<std::string>();
+ const std::string& encoding =
+ args.contains("encoding") ? args.get("encoding").get<std::string>() : "utf-8";
+
+ auto fh = opened_files.find(fh_id);
+
+ if (opened_files.end() == fh) {
+ LogAndReportError(IOException("Invalid FileHandle"), out);
+ return;
+ }
+
+ auto handle = fh->second;
+
+ auto logic = [str, handle, encoding](decltype(out) out) {
+ try {
+ std::vector<std::uint8_t> data;
+ data.resize(str.size());
+
+ if (encoding == "iso-8859-1") {
+ latin1::from_utf8(str, data);
+ } else {
+ LoggerD("copying string memory to vector");
+ std::memcpy(data.data(), str.data(), str.size());
+ }
+ write_file(data.data(), data.size(), handle->file_handle);
+ ReportSuccess(picojson::value{(double)data.size()}, out);
+ } catch (std::runtime_error& e) {
+ LoggerE("Cannot write, cause: %s", e.what());
+ LogAndReportError(IOException(e.what()), out);
+ return;
+ }
+ };
+
+ bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
+
+ if (blocking) {
+ logic(out);
+ return;
+ } else {
+ // Async logic
+ double callback_id = args.get("callbackId").get<double>();
+ this->worker.add_job([this, callback_id, logic] {
+ picojson::value response = picojson::value(picojson::object());
+ picojson::object& async_out = response.get<picojson::object>();
+ async_out["callbackId"] = picojson::value(callback_id);
+ logic(async_out);
+ this->PostMessage(response.serialize().c_str());
+ });
+
+ // Sync return
+ ReportSuccess(out);
+ return;
+ }
+}
+
+void FilesystemInstance::FileHandleReadData(const picojson::value& args, picojson::object& out) {
+ ScopeLogger();
+ CHECK_EXIST(args, "id", out)
+ const int fh_id = static_cast<int>(args.get("id").get<double>());
+ auto fh = opened_files.find(fh_id);
+
+ if (opened_files.end() == fh) {
+ LoggerE("FileHandle with id: %d not found", fh_id);
+ LogAndReportError(IOException("Invalid FileHandle"), out);
+ return;
+ }
+
+ size_t size;
+ if (args.contains("size")) {
+ size = static_cast<size_t>(args.get("size").get<double>());
+ } else {
+ try {
+ size = file_size(fh->second->file_handle);
+ } catch (const std::system_error& e) {
+ LogAndReportError(IOException(e.what()), out);
+ return;
+ }
+ }
+ LoggerD("size: %d", size);
+
+ if (std::string::npos == size) {
+ LogAndReportError(InvalidValuesException("Invalid size was given"), out);
+ return;
+ }
+
+ auto handle = fh->second;
+
+ auto logic = [handle, size](decltype(out) out) {
+ try {
+ std::vector<std::uint8_t> data = read_file(handle->file_handle, size);
+ out["result"] = picojson::value(picojson::string_type, true);
+ encode_binary_in_string(data, out["result"].get<std::string>());
+ } catch (std::runtime_error& e) {
+ LoggerE("Cannot read, cause: %s", e.what());
+ LogAndReportError(IOException(e.what()), out);
+ return;
+ }
+ };
+
+ bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
+
+ if (blocking) {
+ logic(out);
+ return;
+ } else {
+ // Async logic
+ double callback_id = args.get("callbackId").get<double>();
+ this->worker.add_job([this, callback_id, logic] {
+ picojson::value response = picojson::value(picojson::object());
+ picojson::object& async_out = response.get<picojson::object>();
+ async_out["callbackId"] = picojson::value(callback_id);
+ logic(async_out);
+ this->PostMessage(response.serialize().c_str());
+ });
+
+ // Sync return
+ ReportSuccess(out);
+ return;
+ }
+}
+
+void FilesystemInstance::FileHandleWriteData(const picojson::value& args, picojson::object& out) {
+ ScopeLogger();
+ CHECK_EXIST(args, "id", out)
+ CHECK_EXIST(args, "data", out)
+ const auto& str = args.get("data").get<std::string>();
+ const int fh_id = static_cast<int>(args.get("id").get<double>());
+
+ auto fh = opened_files.find(fh_id);
+ if (opened_files.end() == fh) {
+ LoggerE("FileHandle with id: %d not found", fh_id);
+ LogAndReportError(IOException("Invalid FileHandle"), out);
+ return;
+ }
+
+ auto handle = fh->second;
+
+ auto logic = [str, handle](decltype(out) out) {
+ try {
+ std::vector<std::uint8_t> bytes;
+ decode_binary_from_string(str, bytes);
+ write_file(bytes.data(), bytes.size(), handle->file_handle);
+ } catch (std::runtime_error& e) {
+ LogAndReportError(IOException(e.what()), out);
+ return;
+ }
+ };
+
+ bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
+
+ if (blocking) {
+ logic(out);
+ return;
+ } else {
+ // Async logic
+ double callback_id = args.get("callbackId").get<double>();
+ this->worker.add_job([this, callback_id, logic] {
+ picojson::value response = picojson::value(picojson::object());
+ picojson::object& async_out = response.get<picojson::object>();
+ async_out["callbackId"] = picojson::value(callback_id);
+ logic(async_out);
+ this->PostMessage(response.serialize().c_str());
+ });
+
+ // Sync return
+ ReportSuccess(out);
+ return;
+ }
+}
+
+void FilesystemInstance::FileHandleFlush(const picojson::value& args, picojson::object& out) {
+ ScopeLogger();
+ const int fh_id = static_cast<int>(args.get("id").get<double>());
+
+ auto fh = opened_files.find(fh_id);
+ if (opened_files.end() == fh) {
+ LogAndReportError(IOException("Invalid FileHandle"), out);
+ return;
+ }
+
+ auto handle = fh->second;
+
+ auto logic = [handle](decltype(out) out) {
+ try {
+ int ret = fflush(handle->file_handle);
+ if (ret) {
+ int errsv = errno;
+ std::string error_message =
+ std::string("flush failed, error message: ") + strerror(errsv);
+ LogAndReportError(IOException(error_message.c_str()), out);
+ return;
+ }
+ } catch (std::runtime_error& e) {
+ LogAndReportError(IOException(e.what()), out);
+ return;
+ }
+ };
+
+ bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
+
+ if (blocking) {
+ logic(out);
+ return;
+ } else {
+ // Async logic
+ double callback_id = args.get("callbackId").get<double>();
+ this->worker.add_job([this, callback_id, logic] {
+ picojson::value response = picojson::value(picojson::object());
+ picojson::object& async_out = response.get<picojson::object>();
+ async_out["callbackId"] = picojson::value(callback_id);
+ logic(async_out);
+ this->PostMessage(response.serialize().c_str());
+ });
+
+ // Sync return
+ ReportSuccess(out);
+ return;
+ }
+}
+
+void FilesystemInstance::FileHandleSync(const picojson::value& args, picojson::object& out) {
+ ScopeLogger();
+ const int fh_id = static_cast<int>(args.get("id").get<double>());
+
+ auto fh = opened_files.find(fh_id);
+ if (opened_files.end() == fh) {
+ LogAndReportError(IOException("Invalid FileHandle"), out);
+ return;
+ }
+
+ auto handle = fh->second;
+
+ auto logic = [handle](decltype(out) out) {
+ try {
+ int ret = fsync(fileno(handle->file_handle));
+ if (ret) {
+ int errsv = errno;
+ std::string error_message =
+ std::string("sync failed, error message: ") + strerror(errsv);
+ LogAndReportError(IOException(error_message.c_str()), out);
+ return;
+ }
+ } catch (std::runtime_error& e) {
+ LogAndReportError(IOException(e.what()), out);
+ return;
+ }
+ };
+
+ bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
+
+ if (blocking) {
+ logic(out);
+ return;
+ } else {
+ // Async logic
+ double callback_id = args.get("callbackId").get<double>();
+ this->worker.add_job([this, callback_id, logic] {
+ picojson::value response = picojson::value(picojson::object());
+ picojson::object& async_out = response.get<picojson::object>();
+ async_out["callbackId"] = picojson::value(callback_id);
+ logic(async_out);
+ this->PostMessage(response.serialize().c_str());
+ });
+
+ // Sync return
+ ReportSuccess(out);
+ return;
+ }
+}
+
+void FilesystemInstance::FileHandleClose(const picojson::value& args, picojson::object& out) {
+ ScopeLogger();
+ const int fh_id = static_cast<int>(args.get("id").get<double>());
+ auto fh = opened_files.find(fh_id);
+ if (opened_files.end() == fh) {
+ LogAndReportError(IOException("Invalid FileHandle"), out);
+ return;
+ }
+
+ std::shared_ptr<FileHandle> handle = fh->second;
+ opened_files.erase(fh);
+
+ auto logic = [handle](decltype(out) out) {
+ try {
+ if (!handle->file_handle) {
+ LogAndReportError(IOException("File handle already closed."), out);
+ return;
+ }
+ int ret = fclose(handle->file_handle);
+ handle->file_handle = nullptr;
+ if (ret) {
+ int errsv = errno;
+ std::string error_message =
+ std::string("close failed, error message: ") + strerror(errsv);
+ LogAndReportError(IOException(error_message.c_str()), out);
+ return;
+ }
+ } catch (std::runtime_error& e) {
+ LogAndReportError(IOException(e.what()), out);
+ return;
+ }
+ };
+
+ bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
+
+ if (blocking) {
+ bool ready = false;
+ bool done = false;
+ std::mutex mutex;
+ std::condition_variable conditional_variable;
+ // adding empty job to worker's queue, in order to wait for all jobs to be done before closing
+ // FILE*
+ this->worker.add_job([] {},
+ [&conditional_variable, &mutex, &ready, &done, logic, &out] {
+ {
+ // wait for close
+ std::unique_lock<std::mutex> lock(mutex);
+ conditional_variable.wait(lock, [&ready] { return ready; });
+
+ logic(out);
+ done = true;
+ }
+ conditional_variable.notify_one();
+ });
+
+ {
+ // let know that close is ready
+ std::unique_lock<std::mutex> lock(mutex);
+ ready = true;
+ }
+ conditional_variable.notify_one();
+
+ {
+ // wait for worker
+ std::unique_lock<std::mutex> lock(mutex);
+ conditional_variable.wait(lock, [&done] { return done; });
+ }
+ handle->file_handle = nullptr;
+ } else {
+ // Async logic
+ double callback_id = args.get("callbackId").get<double>();
+ this->worker.add_job([this, callback_id, logic] {
+ picojson::value response = picojson::value(picojson::object());
+ picojson::object& async_out = response.get<picojson::object>();
+ async_out["callbackId"] = picojson::value(callback_id);
+ logic(async_out);
+ this->PostMessage(response.serialize().c_str());
+ });
+
+ // Sync return
+ ReportSuccess(out);
+ return;
+ }
+}
+
#undef CHECK_EXIST
} // namespace filesystem