+++ /dev/null
-/*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "filesystem_file.h"
-#include <common/logger.h>
-#include <common/scope_exit.h>
-#include <stdio.h>
-#include <unistd.h>
-
-namespace extension {
-namespace filesystem {
-
-namespace {
-uint8_t characterToNumber(char c) {
- if (c == '+') {
- return 62;
- }
- if (c == '/') {
- return 63;
- }
- if (c <= '9') {
- return c + 0x04;
- }
- if (c <= 'Z') {
- return c - 0x41;
- }
- if (c <= 'z') {
- return c - 0x47;
- }
- return 0;
-}
-
-char numberToCharacter(uint8_t i) {
- if (i <= 25) {
- return 'A' + i;
- }
- if (i <= 51) {
- return 'a' + i - 26;
- }
- if (i <= 61) {
- return '0' + i - 52;
- }
- if (i == 62) {
- return '+';
- }
- if (i == 63) {
- return '/';
- }
- return 0;
-}
-
-bool validateCharacter(char c) {
- if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '=') ||
- (c == '+') || (c == '/')) {
- return true;
- }
- return false;
-}
-} // namespace
-
-/**
- * Data is encoded using Base64 encoding.
- */
-
-bool FilesystemBuffer::DecodeData(const std::string& data) {
- ScopeLogger();
- if (data.length() % 4) {
- LoggerE("Buffer has invalid length");
- return false;
- }
-
- for (auto c : data) {
- if (!validateCharacter(c)) {
- LoggerE("Buffer has invalid character");
- return false;
- }
- }
-
- // Validate padding
- for (size_t i = 0; i + 2 < data.length(); ++i) {
- if (data[i] == '=') {
- LoggerE("Unexpected padding character in string");
- return false;
- }
- }
-
- if (data[data.length() - 2] == '=' && data[data.length() - 1] != '=') {
- LoggerE("Unexpected padding character in string");
- return false;
- }
-
- clear();
-
- if (data.length() == 0) {
- return true;
- }
-
- int padding = 0;
- if (data[data.length() - 1] == '=') {
- padding++;
- }
-
- if (data[data.length() - 2] == '=') {
- padding++;
- }
-
- for (size_t i = 0; i < data.length(); i += 4) {
- uint8_t part[] = {characterToNumber(data[i + 0]), characterToNumber(data[i + 1]),
- characterToNumber(data[i + 2]), characterToNumber(data[i + 3])};
- push_back(uint8_t((part[0] << 2) | (part[1] >> 4)));
- if ((data.length() - i != 4) || (padding < 2)) {
- push_back(uint8_t((part[1] << 4) | (part[2] >> 2)));
- }
- if ((data.length() - i != 4) || (padding < 1)) {
- push_back(uint8_t((part[2] << 6) | (part[3])));
- }
- }
- return true;
-}
-
-std::string FilesystemBuffer::EncodeData() const {
- ScopeLogger();
- std::string out;
-
- for (size_t i = 0; i < size(); i += 3) {
- uint8_t part[] = {safe_get(i), safe_get(i + 1), safe_get(i + 2)};
- out.push_back(numberToCharacter(0x3F & (part[0] >> 2)));
- out.push_back(numberToCharacter(0x3F & ((part[0] << 4) | (part[1] >> 4))));
- out.push_back(numberToCharacter(0x3F & ((part[1] << 2) | (part[2] >> 6))));
- out.push_back(numberToCharacter(0x3F & (part[2])));
- }
-
- if (out.size() == 0) return out;
-
- // Add padding
- int fillup = (size() % 3);
- if (fillup == 1) {
- out[out.size() - 2] = '=';
- }
-
- if (fillup == 1 || fillup == 2) {
- out[out.size() - 1] = '=';
- }
-
- return out;
-}
-
-FilesystemFile::FilesystemFile(const std::string& path_) : path(path_) {
- ScopeLogger();
-}
-
-bool FilesystemFile::Write(const FilesystemBuffer& data, size_t offset, bool rewrite) {
- ScopeLogger();
-
- FILE* file = nullptr;
- if (rewrite) {
- file = fopen(path.c_str(), "w");
- } else {
- file = fopen(path.c_str(), "r+");
- }
-
- if (!file) {
- LoggerE("Cannot open file %s to write!", path.c_str());
- return false;
- }
-
- SCOPE_EXIT {
- int status = fclose(file);
- if (status) {
- LoggerE("Cannot close file!");
- }
- };
-
- int status;
- status = fseek(file, offset, SEEK_SET);
- LoggerD("Offset is %u, writing %i bytes", offset, data.size());
- if (status) {
- LoggerE("Cannot perform seek!");
- return false;
- }
-
- size_t written = 0;
- uint8_t* data_p = const_cast<uint8_t*>(data.data());
- size_t data_size = data.size();
- while (written < data.size()) {
- size_t part = fwrite(data_p, 1, data_size, file);
-
- if (ferror(file)) {
- LoggerE("Error during file write!");
- return false;
- }
-
- written += part;
- data_p += part;
- data_size -= part;
- }
-
- status = fflush(file);
- if (status) {
- LoggerE("Cannot flush file!");
- return false;
- }
-
- status = fsync(fileno(file));
- if (status) {
- LoggerE("Cannot sync file!");
- return false;
- }
- LoggerD("Written %u bytes", written);
-
- return true;
-}
-
-bool FilesystemFile::WriteString(const std::string& data, size_t offset, bool rewrite) {
- ScopeLogger();
-
- FILE* file = nullptr;
- if (rewrite) {
- file = fopen(path.c_str(), "w");
- } else {
- file = fopen(path.c_str(), "r+");
- }
-
- if (!file) {
- LoggerE("Cannot open file %s to write!", path.c_str());
- return false;
- }
-
- SCOPE_EXIT {
- int status = fclose(file);
- if (status) {
- LoggerE("Cannot close file!");
- }
- };
-
- int status;
- status = fseek(file, offset, SEEK_SET);
- LoggerD("Offset is %u, writing %i bytes", offset, data.size());
- if (status) {
- LoggerE("Cannot perform seek!");
- return false;
- }
-
- size_t written = 0;
- uint8_t* data_p = (uint8_t*)(data.c_str());
- size_t data_size = data.size();
-
- while (written < data.size()) {
- size_t part = fwrite(data_p, 1, data_size, file);
-
- if (ferror(file)) {
- LoggerE("Error during file write!");
- return false;
- }
-
- written += part;
- data_p += part;
- data_size -= part;
- }
-
- status = fflush(file);
- if (status) {
- LoggerE("Cannot flush file!");
- return false;
- }
-
- status = fsync(fileno(file));
- if (status) {
- LoggerE("Cannot sync file!");
- return false;
- }
- LoggerD("Written %u bytes", written);
-
- return true;
-}
-} // namespace filesystem
-} // namespace extension
REGISTER_SYNC("File_rename", FileRename);
REGISTER_SYNC("File_readBytes", FileReadBytes);
REGISTER_SYNC("File_readString", FileReadString);
- REGISTER_SYNC("File_write", FileWrite);
- REGISTER_SYNC("File_writeSync", FileWriteSync);
+ REGISTER_SYNC("File_writeBytes", FileWriteBytes);
+ REGISTER_SYNC("File_writeBase64", FileWriteBase64);
+ REGISTER_SYNC("File_writeString", FileWriteString);
REGISTER_SYNC("Filesystem_fetchVirtualRoots", FilesystemFetchVirtualRoots);
REGISTER_SYNC("FileSystemManager_addStorageStateChangeListener", StartListening);
REGISTER_SYNC("FileSystemManager_removeStorageStateChangeListener", StopListening);
std::bind(&FilesystemManager::Rename, &fsm, oldPath, newPath, onSuccess, onError));
}
+/* str should be a reference to (possibly empty) std::string */
+static void encode_binary_in_string(const std::vector<std::uint8_t>& buf, std::string& str) {
+ ScopeLogger();
+ str.reserve(str.size() + buf.size());
+
+ for (std::uint8_t byte : buf) {
+ if (byte >= 128) {
+ str += 0xC0 | (byte >> 6);
+ str += 0x80 | (byte & 0x3F);
+ } else if (byte > 0) {
+ str += byte;
+ } else {
+ // zero as codepoint U+0100
+ str += 0xC4;
+ str += 0x80;
+ }
+ }
+}
+
+/* buf should be a reference to (possibly empty) vector */
+static void decode_binary_from_string(const std::string& str, std::vector<std::uint8_t>& buf) {
+ ScopeLogger();
+ buf.reserve(buf.size() + str.size());
+
+ const std::uint8_t* it = (std::uint8_t*)str.data();
+ const std::uint8_t* end = it + str.size();
+ while (it != end) {
+ if (*it < 128) {
+ buf.push_back(*it++);
+ continue;
+ }
+ auto b1 = *it++;
+ auto b2 = *it++;
+ unsigned int x = ((b1 & 0x1F) << 6) | (b2 & 0x3F);
+ buf.push_back(x != 0x100 ? x : 0);
+ }
+}
+
static constexpr std::size_t NPOS = (std::size_t)(-1);
/**
* Returns a buffer. If length is NPOS, then it reads whole file, up to the end.
return out_buf;
}
-/* str should be reference to (possibly empty) std::string */
-static void encode_binary_in_string(const std::vector<std::uint8_t>& buf, std::string& str) {
+/**
+ * On failure throws std::runtime_error
+ */
+void write_file(const std::uint8_t* data, std::size_t len, std::string path, std::size_t offset,
+ bool rewrite) {
ScopeLogger();
- str.reserve(str.size() + buf.size());
- for (std::uint8_t byte : buf) {
- if (byte >= 128) {
- str += 0xC0 | (byte >> 6);
- str += 0x80 | (byte & 0x3F);
- } else if (byte > 0) {
- str += byte;
- } else {
- // zero as codepoint U+0100
- str += 0xC4;
- str += 0x80;
+ FILE* file = fopen(path.c_str(), rewrite ? "w" : "r+");
+
+ if (!file) {
+ throw std::runtime_error("cannot open file to write");
+ }
+
+ SCOPE_EXIT {
+ int status = std::fclose(file);
+ if (status) {
+ LoggerE("Cannot close file!");
}
+ };
+
+ if (std::fseek(file, offset, SEEK_SET) != 0) {
+ throw std::runtime_error("cannot perform seek");
+ }
+
+ const std::uint8_t* data_p = data;
+ const std::uint8_t* end_p = data + len;
+ while (data_p != end_p) {
+ data_p += fwrite(data_p, 1, end_p - data_p, file);
+
+ if (std::ferror(file)) {
+ throw std::runtime_error("error during file write");
+ }
+ }
+
+ if (std::fflush(file)) {
+ throw std::runtime_error("error during file write");
+ }
+}
+
+namespace base64 {
+static const char int_to_char[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const std::uint8_t INV = 255;
+static const std::uint8_t PAD = 254;
+static const std::uint8_t char_to_int[] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255,
+ 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255};
+
+/**
+ * On error throws std::runtime_error
+ */
+static std::vector<std::uint8_t> decode(const char* str, std::size_t len) {
+ std::vector<std::uint8_t> out;
+ if (len % 4 != 0) throw std::runtime_error("string length is not multiple of 4");
+ if (len == 0) return out;
+
+ const std::uint8_t* p = (std::uint8_t*)str;
+ const std::uint8_t* end = p + len - 4; // last four can have padding in it
+ std::uint8_t b1, b2, b3, b4;
+ out.reserve(len / 4 * 3);
+ while (p != end) {
+ b1 = char_to_int[*p++];
+ b2 = char_to_int[*p++];
+ b3 = char_to_int[*p++];
+ b4 = char_to_int[*p++];
+
+ if (b1 > 63 || b2 > 63 || b3 > 63 || b4 > 63) throw std::runtime_error("invalid character");
+
+ out.push_back((b1 << 2) | (b2 >> 4));
+ out.push_back((b2 << 4) | (b3 >> 2));
+ out.push_back((b3 << 6) | b4);
}
+ b1 = char_to_int[*p++];
+ b2 = char_to_int[*p++];
+ b3 = char_to_int[*p++];
+ b4 = char_to_int[*p++];
+
+ if (b1 == PAD || b1 == INV || b2 == PAD || b2 == INV || b3 == INV || b4 == INV)
+ throw std::runtime_error("invalid character");
+
+ out.push_back((b1 << 2) | (b2 >> 4));
+ if (b3 == PAD) {
+ if (b4 != PAD) throw std::runtime_error("invalid character");
+ } else {
+ out.push_back((b2 << 4) | (b3 >> 2));
+ if (b4 != PAD) out.push_back((b3 << 6) | b4);
+ }
+
+ return out;
}
+} // namespace base64
void FilesystemInstance::FileReadString(const picojson::value& args, picojson::object& out) {
ScopeLogger();
}
}
-void FilesystemInstance::FileWrite(const picojson::value& args, picojson::object& out) {
+void FilesystemInstance::FileWriteString(const picojson::value& args, picojson::object& out) {
ScopeLogger();
- CHECK_EXIST(args, "callbackId", out)
+ CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
CHECK_EXIST(args, "location", out)
CHECK_EXIST(args, "data", out)
CHECK_EXIST(args, "offset", out)
CHECK_EXIST(args, "rewrite", out)
- CHECK_EXIST(args, "isString", out)
+ CHECK_EXIST(args, "encoding", out)
- double callback_id = args.get("callbackId").get<double>();
const std::string& location = args.get("location").get<std::string>();
- const std::string& data = args.get("data").get<std::string>();
- size_t offset = static_cast<size_t>(args.get("location").get<double>());
+ const std::string& str = args.get("data").get<std::string>();
+ size_t offset = static_cast<size_t>(args.get("offset").get<double>());
bool rewrite = static_cast<bool>(args.get("rewrite").get<bool>());
- bool is_string = static_cast<bool>(args.get("isString").get<bool>());
- auto onSuccess = [this, callback_id]() {
- ScopeLogger("Entered into asynchronous function, onSuccess");
- picojson::value response = picojson::value(picojson::object());
- picojson::object& obj = response.get<picojson::object>();
- obj["callbackId"] = picojson::value(callback_id);
- ReportSuccess(obj);
- PostMessage(response.serialize().c_str());
- };
+ try {
+ write_file((const std::uint8_t*)str.c_str(), str.length(), location, offset, rewrite);
+ } catch (std::runtime_error& e) {
+ LoggerE("Cannot write to file %s, cause: %s", location.c_str(), e.what());
+ PrepareError(FilesystemError::Other, out);
+ }
+ ReportSuccess(out);
+}
- auto onError = [this, callback_id](FilesystemError e) {
- ScopeLogger("Entered into asynchronous function, onError");
- picojson::value response = picojson::value(picojson::object());
- picojson::object& obj = response.get<picojson::object>();
- obj["callbackId"] = picojson::value(callback_id);
- PrepareError(e, obj);
- PostMessage(response.serialize().c_str());
- };
+void FilesystemInstance::FileWriteBytes(const picojson::value& args, picojson::object& out) {
+ ScopeLogger();
+ CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
+ CHECK_EXIST(args, "location", out)
+ CHECK_EXIST(args, "data", out)
+ CHECK_EXIST(args, "offset", out)
+ CHECK_EXIST(args, "rewrite", out)
- FilesystemManager& fsm = FilesystemManager::GetInstance();
- common::TaskQueue::GetInstance().Async(std::bind(&FilesystemManager::FileWrite, &fsm, location,
- data, offset, rewrite, is_string, onSuccess,
- onError));
+ const std::string& location = args.get("location").get<std::string>();
+ const std::string& str = args.get("data").get<std::string>();
+ size_t offset = static_cast<size_t>(args.get("offset").get<double>());
+ bool rewrite = static_cast<bool>(args.get("rewrite").get<bool>());
+
+ try {
+ std::vector<std::uint8_t> data;
+ decode_binary_from_string(str, data);
+ write_file(data.data(), data.size(), location, offset, rewrite);
+ } catch (std::runtime_error& e) {
+ LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what());
+ PrepareError(FilesystemError::Other, out);
+ }
+ ReportSuccess(out);
}
-void FilesystemInstance::FileWriteSync(const picojson::value& args, picojson::object& out) {
+void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson::object& out) {
ScopeLogger();
CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
CHECK_EXIST(args, "location", out)
CHECK_EXIST(args, "data", out)
CHECK_EXIST(args, "offset", out)
CHECK_EXIST(args, "rewrite", out)
- CHECK_EXIST(args, "isString", out)
const std::string& location = args.get("location").get<std::string>();
- const std::string& data = args.get("data").get<std::string>();
+ const std::string& str = args.get("data").get<std::string>();
size_t offset = static_cast<size_t>(args.get("offset").get<double>());
bool rewrite = static_cast<bool>(args.get("rewrite").get<bool>());
- bool is_string = static_cast<bool>(args.get("isString").get<bool>());
-
- auto onSuccess = [this, &out]() {
- ScopeLogger("Entered into asynchronous function, onSuccess");
- ReportSuccess(out);
- };
- auto onError = [this, &out](FilesystemError e) {
- ScopeLogger("Entered into asynchronous function, onError");
- PrepareError(e, out);
- };
+ std::vector<std::uint8_t> data;
+ try {
+ data = base64::decode(str.c_str(), str.length());
+ } catch (std::runtime_error& e) {
+ LoggerE("Can't decode base64: %s", e.what());
+ ReportError(InvalidValuesException(std::string("Can't decode base64: ") + e.what()), out);
+ return;
+ }
- FilesystemManager::GetInstance().FileWrite(location, data, offset, rewrite, is_string, onSuccess,
- onError);
+ try {
+ write_file(data.data(), data.size(), location, offset, rewrite);
+ ReportSuccess(picojson::value{(double)data.size()}, out);
+ } catch (std::runtime_error& e) {
+ LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what());
+ PrepareError(FilesystemError::Other, out);
+ }
}
void FilesystemInstance::FileStat(const picojson::value& args, picojson::object& out) {