From 58800e76ff8c14fcd6a79496806125af3502ed04 Mon Sep 17 00:00:00 2001 From: Kamil Lysik Date: Tue, 17 Feb 2015 13:44:05 +0100 Subject: [PATCH] [Filesystem] File read and write native API Change-Id: If8aadda4ae5cf167d3e2c974a78c0da4df267466 Signed-off-by: Kamil Lysik --- src/filesystem/filesystem.gyp | 2 + src/filesystem/filesystem_file.cc | 260 ++++++++++++++++++++++++++++++++++ src/filesystem/filesystem_file.h | 42 ++++++ src/filesystem/filesystem_instance.cc | 137 ++++++++++++++++++ src/filesystem/filesystem_instance.h | 4 + src/filesystem/filesystem_manager.cc | 91 +++++++++--- src/filesystem/filesystem_manager.h | 15 +- 7 files changed, 529 insertions(+), 22 deletions(-) create mode 100644 src/filesystem/filesystem_file.cc create mode 100644 src/filesystem/filesystem_file.h diff --git a/src/filesystem/filesystem.gyp b/src/filesystem/filesystem.gyp index a8fcba9..9c49be7 100644 --- a/src/filesystem/filesystem.gyp +++ b/src/filesystem/filesystem.gyp @@ -16,6 +16,8 @@ 'filesystem_api.js', 'filesystem_extension.cc', 'filesystem_extension.h', + 'filesystem_file.cc', + 'filesystem_file.h', 'filesystem_instance.cc', 'filesystem_instance.h', 'filesystem_manager.cc', diff --git a/src/filesystem/filesystem_file.cc b/src/filesystem/filesystem_file.cc new file mode 100644 index 0000000..eb810e5 --- /dev/null +++ b/src/filesystem/filesystem_file.cc @@ -0,0 +1,260 @@ +// Copyright 2015 Samsung Electronics Co, Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "filesystem_file.h" +#include +#include +#include +#include + +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) { + 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 { + 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_) {} + +bool FilesystemFile::Read(FilesystemBuffer* data, + size_t offset, + size_t length) { + if (!data) { + LoggerE("Missing output buffer"); + return false; + } + + data->resize(length); + FILE* file = fopen(path.c_str(), "r"); + if (!file) { + LoggerE("Cannot open file %s to read!", 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); + if (status) { + LoggerE("Cannot perform seek!"); + return false; + } + + size_t readed = 0; + uint8_t* data_p = data->data(); + size_t data_size = length; + while (readed < data->size()) { + size_t part = fread(data_p, 1, data_size, file); + + readed += part; + data_p += part; + data_size -= part; + + LoggerD("Readed part %li bytes", readed); + + if (ferror(file)) { + LoggerE("Error during file write!"); + return false; + } + + if (feof(file)) { + LoggerD("File is at end before buffer is filled. Finish."); + break; + } + } + LoggerD("Readed %li bytes", readed); + data->resize(readed); + return true; +} + +bool FilesystemFile::Write(const FilesystemBuffer& data, size_t offset) { + FILE* 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 %li, writing %i bytes", offset, data.size()); + if (status) { + LoggerE("Cannot perform seek!"); + return false; + } + + size_t written = 0; + uint8_t* data_p = const_cast(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 %li bytes", written); + + return true; +} +} // namespace filesystem +} // namespace extension diff --git a/src/filesystem/filesystem_file.h b/src/filesystem/filesystem_file.h new file mode 100644 index 0000000..8746cff --- /dev/null +++ b/src/filesystem/filesystem_file.h @@ -0,0 +1,42 @@ +// Copyright 2015 Samsung Electronics Co, Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FILESYSTEM_FILESYSTEM_FILE_H +#define FILESYSTEM_FILESYSTEM_FILE_H + +#include +#include +#include + +namespace extension { +namespace filesystem { + +class FilesystemBuffer : public std::vector { + public: + bool DecodeData(const std::string& data); + std::string EncodeData() const; + + private: + inline uint8_t safe_get(size_t i) const { + if (i >= size()) { + return 0; + } + return at(i); + } +}; + +class FilesystemFile { + const std::string path; + + public: + FilesystemFile(const std::string& path_); + + bool Read(FilesystemBuffer* data, size_t offset, size_t length); + bool Write(const FilesystemBuffer& data, size_t offset); +}; + +} // namespace filesystem +} // namespace extension + +#endif // FILESYSTEM_FILESYSTEM_FILE_H diff --git a/src/filesystem/filesystem_instance.cc b/src/filesystem/filesystem_instance.cc index 8d3dd31..6757b56 100644 --- a/src/filesystem/filesystem_instance.cc +++ b/src/filesystem/filesystem_instance.cc @@ -35,6 +35,10 @@ FilesystemInstance::FilesystemInstance() { REGISTER_SYNC("File_createSync", FileCreateSync); REGISTER_ASYNC("File_readDir", ReadDir); REGISTER_ASYNC("File_rename", FileRename); + REGISTER_ASYNC("File_read", FileRead); + REGISTER_SYNC("File_readSync", FileReadSync); + REGISTER_ASYNC("File_write", FileWrite); + REGISTER_SYNC("File_writeSync", FileWriteSync); REGISTER_SYNC("Filesystem_getWidgetPaths", FilesystemGetWidgetPaths); REGISTER_SYNC("FileSystemManager_addStorageStateChangeListener", StartListening); @@ -114,6 +118,139 @@ void FilesystemInstance::FileRename(const picojson::value& args, &FilesystemManager::Rename, &fsm, oldPath, newPath, onSuccess, onError)); } +void FilesystemInstance::FileRead(const picojson::value& args, + picojson::object& out) { + LoggerD("enter"); + CHECK_EXIST(args, "callbackId", out) + CHECK_EXIST(args, "location", out) + CHECK_EXIST(args, "offset", out) + CHECK_EXIST(args, "length", out) + + double callback_id = args.get("callbackId").get(); + const std::string& location = args.get("location").get(); + size_t offset = static_cast(args.get("offset").get()); + size_t length = static_cast(args.get("length").get()); + + auto onSuccess = [this, callback_id](const std::string& data) { + LoggerD("enter"); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + ReportSuccess(picojson::value(data), obj); + PostMessage(response.serialize().c_str()); + }; + + auto onError = [this, callback_id](FilesystemError e) { + LoggerD("enter"); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + PrepareError(e, obj); + PostMessage(response.serialize().c_str()); + }; + + FilesystemManager& fsm = FilesystemManager::GetInstance(); + common::TaskQueue::GetInstance().Async(std::bind(&FilesystemManager::FileRead, + &fsm, + location, + offset, + length, + onSuccess, + onError)); +} + +void FilesystemInstance::FileReadSync(const picojson::value& args, + picojson::object& out) { + LoggerD("enter"); + CHECK_EXIST(args, "location", out) + CHECK_EXIST(args, "offset", out) + CHECK_EXIST(args, "length", out) + + const std::string& location = args.get("location").get(); + size_t offset = static_cast(args.get("offset").get()); + size_t length = static_cast(args.get("length").get()); + + auto onSuccess = [this, &out](const std::string& data) { + LoggerD("enter"); + ReportSuccess(picojson::value(data), out); + }; + + auto onError = [this, &out](FilesystemError e) { + LoggerD("enter"); + PrepareError(e, out); + }; + + FilesystemManager::GetInstance().FileRead( + location, offset, length, onSuccess, onError); +} + +void FilesystemInstance::FileWrite(const picojson::value& args, + picojson::object& out) { + LoggerD("enter"); + CHECK_EXIST(args, "callbackId", out) + CHECK_EXIST(args, "location", out) + CHECK_EXIST(args, "data", out) + CHECK_EXIST(args, "offset", out) + + double callback_id = args.get("callbackId").get(); + const std::string& location = args.get("location").get(); + const std::string& data = args.get("data").get(); + size_t offset = static_cast(args.get("location").get()); + + auto onSuccess = [this, callback_id]() { + LoggerD("enter"); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + ReportSuccess(obj); + PostMessage(response.serialize().c_str()); + }; + + auto onError = [this, callback_id](FilesystemError e) { + LoggerD("enter"); + picojson::value response = picojson::value(picojson::object()); + picojson::object& obj = response.get(); + obj["callbackId"] = picojson::value(callback_id); + PrepareError(e, obj); + PostMessage(response.serialize().c_str()); + }; + + FilesystemManager& fsm = FilesystemManager::GetInstance(); + common::TaskQueue::GetInstance().Async( + std::bind(&FilesystemManager::FileWrite, + &fsm, + location, + data, + offset, + onSuccess, + onError)); +} + +void FilesystemInstance::FileWriteSync(const picojson::value& args, + picojson::object& out) { + LoggerD("enter"); + CHECK_EXIST(args, "location", out) + CHECK_EXIST(args, "data", out) + CHECK_EXIST(args, "offset", out) + + const std::string& location = args.get("location").get(); + const std::string& data = args.get("data").get(); + size_t offset = static_cast(args.get("offset").get()); + + auto onSuccess = [this, &out]() { + LoggerD("enter"); + ReportSuccess(out); + }; + + auto onError = [this, &out](FilesystemError e) { + LoggerD("enter"); + PrepareError(e, out); + }; + + FilesystemManager::GetInstance().FileWrite( + location, data, offset, onSuccess, onError); +} + void FilesystemInstance::FileStat(const picojson::value& args, picojson::object& out) { LoggerD("enter"); diff --git a/src/filesystem/filesystem_instance.h b/src/filesystem/filesystem_instance.h index 9f69e8d..7bb408a 100644 --- a/src/filesystem/filesystem_instance.h +++ b/src/filesystem/filesystem_instance.h @@ -23,6 +23,10 @@ class FilesystemInstance : public common::ParsedInstance, void FileRename(const picojson::value& args, picojson::object& out); void FileStat(const picojson::value& args, picojson::object& out); void FileStatSync(const picojson::value& args, picojson::object& out); + void FileRead(const picojson::value& args, picojson::object& out); + void FileReadSync(const picojson::value& args, picojson::object& out); + void FileWrite(const picojson::value& args, picojson::object& out); + void FileWriteSync(const picojson::value& args, picojson::object& out); void FilesystemGetWidgetPaths(const picojson::value& args, picojson::object& out); void FileSystemManagerFetchStorages(const picojson::value& args, diff --git a/src/filesystem/filesystem_manager.cc b/src/filesystem/filesystem_manager.cc index 7af0624..4a725b9 100644 --- a/src/filesystem/filesystem_manager.cc +++ b/src/filesystem/filesystem_manager.cc @@ -20,6 +20,7 @@ #include "common/logger.h" #include "common/scope_exit.h" #include "common/extension.h" +#include "filesystem_file.h" namespace extension { namespace filesystem { @@ -34,12 +35,15 @@ void storage_cb(int storage_id, storage_state_e state, void* user_data) { storage_get_type(storage_id, &type); listener->onFilesystemStateChangeSuccessCallback( std::to_string(type) + std::to_string(storage_id), - std::to_string(state), std::to_string(type)); + std::to_string(state), + std::to_string(type)); } } -int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) -{ +int unlink_cb(const char* fpath, + const struct stat* sb, + int typeflag, + struct FTW* ftwbuf) { int result = remove(fpath); if (result) LoggerE("error occured"); @@ -101,7 +105,7 @@ void FilesystemManager::FetchStorages( if (STORAGE_ERROR_NONE != storage_foreach_device_supported(fetch_storages_cb, &result)) error_cb(FilesystemError::Other); - for(auto storage : result) { + for (auto storage : result) { ids_.insert(storage.storage_id); } success_cb(result); @@ -256,22 +260,23 @@ void FilesystemManager::Rename( } void FilesystemManager::ReadDir( - const std::string& path, - const std::function&)>& success_cb, - const std::function& error_cb) { + const std::string& path, + const std::function&)>& success_cb, + const std::function& error_cb) { LoggerD("entered"); std::vector fileList; - DIR *dp = nullptr; + DIR* dp = nullptr; struct dirent entry; - struct dirent *result = nullptr; + struct dirent* result = nullptr; int status = 0; dp = opendir(path.c_str()); if (dp != NULL) { - while ((status = readdir_r(dp, &entry, &result)) == 0 && result != nullptr) { - if (strcmp(result->d_name, ".") != 0 && strcmp(result->d_name, "..") != 0) - fileList.push_back(path + "/" + std::string(result->d_name)); + while ((status = readdir_r(dp, &entry, &result)) == 0 && + result != nullptr) { + if (strcmp(result->d_name, ".") != 0 && strcmp(result->d_name, "..") != 0) + fileList.push_back(path + "/" + std::string(result->d_name)); } (void)closedir(dp); if (status == 0) { @@ -288,21 +293,21 @@ void FilesystemManager::ReadDir( } void FilesystemManager::UnlinkFile( - const std::string& path, - const std::function& success_cb, - const std::function& error_cb) { + const std::string& path, + const std::function& success_cb, + const std::function& error_cb) { if (unlink(path.c_str()) != 0) { - LoggerE("Error occured while deleting file"); - error_cb(FilesystemError::Other); - return; + LoggerE("Error occured while deleting file"); + error_cb(FilesystemError::Other); + return; } success_cb(); } void FilesystemManager::RemoveDirectory( - const std::string& path, - const std::function& success_cb, - const std::function& error_cb) { + const std::string& path, + const std::function& success_cb, + const std::function& error_cb) { const int maxDirOpened = 64; if (nftw(path.c_str(), unlink_cb, maxDirOpened, FTW_DEPTH | FTW_PHYS) != 0) { LoggerE("Error occured"); @@ -311,6 +316,50 @@ void FilesystemManager::RemoveDirectory( success_cb(); return; } + +void FilesystemManager::FileRead( + const std::string& path, + size_t offset, + size_t length, + const std::function& success_cb, + const std::function& error_cb) { + + FilesystemFile file(path); + FilesystemBuffer buffer; + if (!file.Read(&buffer, offset, length)) { + LoggerE("Cannot read file %s", path.c_str()); + error_cb(FilesystemError::Other); + return; + } + + std::string out_data = buffer.EncodeData(); + success_cb(out_data); +} + +void FilesystemManager::FileWrite( + const std::string& path, + const std::string& data, + size_t offset, + const std::function& success_cb, + const std::function& error_cb) { + + FilesystemFile file(path); + FilesystemBuffer buffer; + // Decode buffer data + if (!buffer.DecodeData(data)) { + LoggerE("Cannot decode file data!"); + error_cb(FilesystemError::Other); + return; + } + + if (file.Write(buffer, offset)) { + success_cb(); + } else { + LoggerE("Cannot write to file %s!", path.c_str()); + error_cb(FilesystemError::Other); + } +} + void FilesystemManager::StartListening() { LoggerD("enter"); diff --git a/src/filesystem/filesystem_manager.h b/src/filesystem/filesystem_manager.h index d1d44c1..d25a814 100644 --- a/src/filesystem/filesystem_manager.h +++ b/src/filesystem/filesystem_manager.h @@ -63,7 +63,7 @@ class FilesystemManager { void MakeDirectory(const std::string& path, const std::function& result_cb); - + void ReadDir( const std::string& path, const std::function&)>& success_cb, @@ -73,6 +73,19 @@ class FilesystemManager { const std::string& path, const std::function& success_cb, const std::function& error_cb); + + void FileRead(const std::string& path, + size_t offset, + size_t length, + const std::function& success_cb, + const std::function& error_cb); + + void FileWrite(const std::string& path, + const std::string& data, + size_t offset, + const std::function& success_cb, + const std::function& error_cb); + void StartListening(); void StopListening(); void AddListener(FilesystemStateChangeListener* listener); -- 2.7.4