2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "filesystem/filesystem_instance.h"
19 #include <linux/limits.h>
25 #include <system_error>
26 #include "common/logger.h"
27 #include "common/picojson.h"
28 #include "common/platform_exception.h"
29 #include "common/scope_exit.h"
30 #include "common/task-queue.h"
31 #include "common/tools.h"
32 #include "filesystem_manager.h"
35 namespace filesystem {
39 using common::tools::GetErrorString;
41 // The privileges that required in Filesystem API
42 const std::string kPrivilegeFilesystemRead = "http://tizen.org/privilege/filesystem.read";
43 const std::string kPrivilegeFilesystemWrite = "http://tizen.org/privilege/filesystem.write";
45 const std::string kISOEncoding = "ISO-8859-1";
46 const std::string kUTF8Encoding = "UTF-8";
48 std::string GetFopenMode(const picojson::value& args) {
50 const std::string& open_mode = args.get("openMode").get<std::string>();
52 if ("rw" == open_mode) {
54 } else if ("rwo" == open_mode) {
61 bool WriteAccessRequested(const picojson::value& args) {
63 const std::string& open_mode = args.get("openMode").get<std::string>();
65 return "a" == open_mode || "rw" == open_mode || "rwo" == open_mode || "w" == open_mode;
68 bool ReadAccessRequested(const picojson::value& args) {
70 const std::string& open_mode = args.get("openMode").get<std::string>();
72 return "r" == open_mode || "rw" == open_mode || "rwo" == open_mode;
75 bool ShouldMakeParents(const picojson::value& args) {
77 const std::string& path = args.get("path").get<std::string>();
79 if (FilesystemUtils::CheckIfExists(FilesystemUtils::Dirname(path), &buf)) {
82 bool make_parents = args.get("makeParents").get<bool>();
83 const std::string& open_mode = args.get("openMode").get<std::string>();
84 return make_parents && ("w" == open_mode || "a" == open_mode || "rwo" == open_mode);
88 using namespace common;
89 using namespace extension::filesystem;
90 using namespace std::string_literals;
92 FileHandle::~FileHandle() {
95 if (file_handle && std::fclose(file_handle)) {
96 LoggerE("close file failed, error message: %s", GetErrorString(errno).c_str());
100 FilesystemInstance::FilesystemInstance() {
103 using std::placeholders::_1;
104 using std::placeholders::_2;
106 #define REGISTER_SYNC(c, x) RegisterSyncHandler(c, std::bind(&FilesystemInstance::x, this, _1, _2));
108 REGISTER_SYNC("File_stat", FileStat);
109 REGISTER_SYNC("File_statSync", FileStatSync);
110 REGISTER_SYNC("File_createSync", FileCreateSync);
111 REGISTER_SYNC("File_readDir", ReadDir);
112 REGISTER_SYNC("File_rename", FileRename);
113 REGISTER_SYNC("File_readBytes", FileReadBytes);
114 REGISTER_SYNC("File_readString", FileReadString);
115 REGISTER_SYNC("File_writeBytes", FileWriteBytes);
116 REGISTER_SYNC("File_writeBase64", FileWriteBase64);
117 REGISTER_SYNC("File_writeString", FileWriteString);
118 REGISTER_SYNC("Filesystem_fetchVirtualRoots", FilesystemFetchVirtualRoots);
119 REGISTER_SYNC("FileSystemManager_addStorageStateChangeListener", StartListening);
120 REGISTER_SYNC("FileSystemManager_removeStorageStateChangeListener", StopListening);
121 REGISTER_SYNC("FileSystemManager_fetchStorages", FileSystemManagerFetchStorages);
122 REGISTER_SYNC("FileSystemManager_mkdir", FileSystemManagerMakeDirectory);
123 REGISTER_SYNC("FileSystemManager_mkdirSync", FileSystemManagerMakeDirectorySync);
124 REGISTER_SYNC("File_unlinkFile", UnlinkFile);
125 REGISTER_SYNC("File_removeDirectory", RemoveDirectory);
126 REGISTER_SYNC("File_copyTo", CopyTo);
127 REGISTER_SYNC("FileSystemManager_getCanonicalPath", FileSystemManagerGetCanonicalPath);
129 REGISTER_SYNC("FileSystemManager_openFile", FileSystemManagerOpenFile);
130 REGISTER_SYNC("FileSystemManager_createDirectory", FileSystemManagerCreateDirectory);
131 REGISTER_SYNC("FileSystemManager_deleteFile", FileSystemManagerDeleteFile);
132 REGISTER_SYNC("FileSystemManager_deleteDirectory", FileSystemManagerDeleteDirectory);
133 REGISTER_SYNC("FileSystemManager_copyFile", FileSystemManagerCopyFile);
134 REGISTER_SYNC("FileSystemManager_copyDirectory", FileSystemManagerCopyDirectory);
135 REGISTER_SYNC("FileSystemManager_moveFile", FileSystemManagerMoveFile);
136 REGISTER_SYNC("FileSystemManager_moveDirectory", FileSystemManagerMoveDirectory);
137 REGISTER_SYNC("FileSystemManager_rename", FileSystemManagerRename);
138 REGISTER_SYNC("FileSystemManager_listDirectory", FileSystemManagerListDirectory);
139 REGISTER_SYNC("FileSystemManager_isFile", FileSystemManagerIsFile);
140 REGISTER_SYNC("FileSystemManager_isDirectory", FileSystemManagerIsDirectory);
141 REGISTER_SYNC("FileSystemManager_pathExists", FileSystemManagerPathExists);
142 REGISTER_SYNC("FileSystemManager_getLimits", FileSystemManagerGetLimits);
144 REGISTER_SYNC("FileHandle_seek", FileHandleSeek);
145 REGISTER_SYNC("FileHandle_readString", FileHandleReadString);
146 REGISTER_SYNC("FileHandle_writeString", FileHandleWriteString);
147 REGISTER_SYNC("FileHandle_readData", FileHandleReadData);
148 REGISTER_SYNC("FileHandle_writeData", FileHandleWriteData);
149 REGISTER_SYNC("FileHandle_flush", FileHandleFlush);
150 REGISTER_SYNC("FileHandle_sync", FileHandleSync);
151 REGISTER_SYNC("FileHandle_close", FileHandleClose);
154 FilesystemManager::GetInstance().AddListener(this);
157 FilesystemInstance::~FilesystemInstance() {
160 FilesystemManager::GetInstance().StopListening();
161 FilesystemManager::GetInstance().RemoveListener();
164 #define CHECK_EXIST(args, name, out) \
165 if (!args.contains(name)) { \
166 LogAndReportError(TypeMismatchException(name " is required argument"), out); \
170 void FilesystemInstance::FileCreateSync(const picojson::value& args, picojson::object& out) {
173 "DEPRECATION WARNING: File.createFile() is deprecated since Tizen 5.0. Use "
174 "FileSystemManager.openFile() instead.");
176 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
177 CHECK_EXIST(args, "location", out)
178 const std::string& location = args.get("location").get<std::string>();
179 CHECK_STORAGE_ACCESS(location, &out);
181 auto onSuccess = [&](const FilesystemStat& data) {
182 ScopeLogger("Entered into asynchronous function, onSuccess");
183 ReportSuccess(data.toJSON(), out);
186 auto onError = [&](FilesystemError e) {
187 ScopeLogger("Entered into asynchronous function, onError");
188 PrepareError(e, out);
191 FilesystemManager::GetInstance().CreateFile(location, onSuccess, onError);
194 void FilesystemInstance::FileRename(const picojson::value& args, picojson::object& out) {
197 "DEPRECATION WARNING: File.moveTo() is deprecated since Tizen 5.0. Use "
198 "FileSystemManager.moveFile() or FileSystemManager.moveDirectory() instead.");
200 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
201 CHECK_EXIST(args, "callbackId", out)
203 CHECK_EXIST(args, "oldPath", out)
204 const std::string& oldPath = args.get("oldPath").get<std::string>();
205 CHECK_STORAGE_ACCESS(oldPath, &out);
206 CHECK_EXIST(args, "newPath", out)
207 const std::string& newPath = args.get("newPath").get<std::string>();
208 CHECK_STORAGE_ACCESS(newPath, &out);
210 double callback_id = args.get("callbackId").get<double>();
212 auto onSuccess = [this, callback_id](const FilesystemStat& data) {
213 ScopeLogger("Entered into asynchronous function, onSuccess");
214 picojson::value response = picojson::value(picojson::object());
215 picojson::object& obj = response.get<picojson::object>();
216 obj["callbackId"] = picojson::value(callback_id);
217 ReportSuccess(data.toJSON(), obj);
218 Instance::PostMessage(this, response.serialize().c_str());
221 auto onError = [this, callback_id](FilesystemError e) {
222 ScopeLogger("Entered into asynchronous function, onError");
223 picojson::value response = picojson::value(picojson::object());
224 picojson::object& obj = response.get<picojson::object>();
225 obj["callbackId"] = picojson::value(callback_id);
226 PrepareError(e, obj);
227 Instance::PostMessage(this, response.serialize().c_str());
230 FilesystemManager& fsm = FilesystemManager::GetInstance();
231 common::TaskQueue::GetInstance().Async(
232 std::bind(&FilesystemManager::Rename, &fsm, oldPath, newPath, onSuccess, onError));
235 /* Write to str buf bytes as if they were UTF-8 codepoints */
236 static void encode_binary_in_string(const std::vector<std::uint8_t>& buf, std::string& str) {
238 str.reserve(str.size() + buf.size());
240 for (std::uint8_t byte : buf) {
245 str += 0xC0 | (byte >> 6);
246 str += 0x80 | (byte & 0x3F);
250 /* Decode (max 2-byte) UTF-8 characters to buf, throws std::runtime_error */
251 static void decode_binary_from_string(const std::string& str, std::vector<std::uint8_t>& buf) {
253 buf.reserve(buf.size() + str.size());
255 const std::uint8_t* it = (std::uint8_t*)str.data();
256 const std::uint8_t* end = it + str.size();
259 buf.push_back(*it++);
264 throw std::runtime_error("internal error (invalid UTF-8 sequence)");
267 unsigned int x = ((b1 & 0x1F) << 6) | (b2 & 0x3F);
273 static auto to_utf8 = &encode_binary_in_string;
274 /* It does not check if UTF-8 values are representable by ISO-8859-1. Make proper checks and
275 * substitute invalid characters in JavaScript before passing through crosswalk */
276 static auto from_utf8 = &decode_binary_from_string;
279 static constexpr std::size_t NPOS = (std::size_t)(-1);
282 * On failure throws std::system_error
284 static std::size_t file_size(FILE* file) {
288 int status = ::fstat(::fileno(file), &buf);
290 throw std::system_error{errno, std::generic_category(), "failed to get file size"};
297 * Returns the amount of bytes to the EOF starting from current file-position indicator.
299 * On failure throws std::system_error
301 static std::size_t file_bytes_to_eof(FILE* file) {
304 std::size_t total_fize_size = file_size(file);
305 long file_position = ftell(file);
306 if (-1 == file_position) {
307 throw std::system_error{errno, std::generic_category(),
308 "Failed to get file position"s + GetErrorString(errno)};
310 return total_fize_size - static_cast<size_t>(file_position);
313 static std::vector<std::uint8_t> read_file(FILE* file, std::size_t length = NPOS,
314 std::size_t* read_bytes = nullptr);
317 * Returns a buffer. If length is NPOS, then it reads whole file, up to the end.
318 * On failure throws std::runtime_error
320 static std::vector<std::uint8_t> read_file(std::string path, long offset = 0,
321 std::size_t length = NPOS) {
324 FILE* file = std::fopen(path.c_str(), "r");
326 std::string err_msg = std::string("Cannot open file to read. ") + GetErrorString(errno);
327 throw std::system_error{errno, std::generic_category(), err_msg};
331 int status = std::fclose(file);
333 LoggerE("Cannot close file!");
337 if (0 != offset && 0 != std::fseek(file, offset, SEEK_SET)) {
338 std::string err_msg = std::string("Cannot perform seek. ") + GetErrorString(errno);
339 throw std::system_error{errno, std::generic_category(), err_msg};
342 if (NPOS == length) {
343 length = file_size(file) - offset;
346 return read_file(file, length);
350 * Returns a buffer. If length is NPOS, then it reads whole file, up to the end.
351 * On failure throws std::runtime_error
353 static std::vector<std::uint8_t> read_file(FILE* file, std::size_t length /*= NPOS*/,
354 std::size_t* read_bytes /* = nullptr*/) {
357 // By default reads whole file. Get the file size.
358 if (NPOS == length) {
359 length = file_size(file);
362 std::vector<std::uint8_t> out_buf;
364 out_buf.resize(length);
365 } catch (std::bad_alloc& err) {
366 throw std::runtime_error{"Could not allocate memory"};
368 std::uint8_t* data_p = out_buf.data();
369 std::uint8_t* end_p = data_p + length;
370 while (data_p != end_p) {
371 data_p += std::fread(data_p, 1, end_p - data_p, file);
373 if (std::ferror(file)) {
374 std::string err_msg = std::string("Error during file read. ") + GetErrorString(errno);
375 throw std::runtime_error(err_msg);
378 if (std::feof(file)) {
379 LoggerD("File is at end before buffer is filled. Finish.");
383 // read_file function is used in API since version 1.0.
384 // read_bytes was added in Tizen 5.0, with default value equal to nullptr, the behaviour is not
386 // It is used to return the actual number of read bytes, because requested length might be bigger
387 // than possible bytes to be read.
388 if (nullptr != read_bytes) {
389 *read_bytes = std::distance(out_buf.data(), data_p);
395 * On failure throws std::runtime_error
397 void write_file(const std::uint8_t* data, std::size_t len, FILE* file) {
400 const std::uint8_t* data_p = data;
401 const std::uint8_t* end_p = data + len;
402 while (data_p != end_p) {
403 data_p += fwrite(data_p, 1, end_p - data_p, file);
405 if (std::ferror(file)) {
406 std::string err_msg = std::string("Error during file write. ") + GetErrorString(errno);
407 throw std::runtime_error(err_msg);
411 if (std::fflush(file)) {
412 std::string err_msg = std::string("Error during file write. ") + GetErrorString(errno);
413 throw std::runtime_error(err_msg);
418 * On failure throws std::runtime_error
420 void write_file(const std::uint8_t* data, std::size_t len, std::string path, long offset,
424 FILE* file = std::fopen(path.c_str(), mode);
427 std::string err_msg = std::string("Cannot open file to write. ") + GetErrorString(errno);
428 throw std::runtime_error(err_msg);
432 int status = std::fclose(file);
434 LoggerE("Cannot close file!");
438 if (offset != 0 && std::fseek(file, offset, SEEK_SET) != 0) {
439 std::string err_msg = std::string("Cannot perform seek. ") + GetErrorString(errno);
440 throw std::system_error{errno, std::generic_category(), err_msg};
443 write_file(data, len, file);
446 #define FIRST_BIT_MASK 0x80
447 #define SECOND_BIT_MASK 0x40
448 #define THIRD_BIT_MASK 0x20
449 #define FOURTH_BIT_MASK 0x10
450 #define FIFTH_BIT_MASK 0x08
452 enum class ByteOfUTF8Classification {
461 ByteOfUTF8Classification is_utf8_byte(uint8_t byte) {
462 if (FIRST_BIT_MASK & byte) {
463 if (SECOND_BIT_MASK & byte) {
464 if (THIRD_BIT_MASK & byte) {
465 if (FOURTH_BIT_MASK & byte) {
466 if (FIFTH_BIT_MASK & byte) {
467 return ByteOfUTF8Classification::NOT_VALID;
469 return ByteOfUTF8Classification::FIRST_OF_4_BYTES;
472 return ByteOfUTF8Classification::FIRST_OF_3_BYTES;
475 return ByteOfUTF8Classification::FIRST_OF_2_BYTES;
478 return ByteOfUTF8Classification::EXTENSION_BYTE;
481 return ByteOfUTF8Classification::ONE_BYTE_CHAR;
485 void skip_partial_character(std::vector<std::uint8_t>& buf) {
486 auto buf_position = buf.begin();
488 while (buf.end() != buf_position &&
489 (ByteOfUTF8Classification::EXTENSION_BYTE == is_utf8_byte(*buf_position))) {
490 buf_position = buf.erase(buf_position);
491 LoggerD("Removed UTF-8 Extension Byte from begining of read buffer");
495 bool validate_and_check_character_count(std::vector<std::uint8_t>& buf, unsigned long& char_count,
496 short& no_of_extensions_expected) {
499 no_of_extensions_expected = 0;
500 auto buf_position = buf.begin();
502 skip_partial_character(buf);
504 while (buf.end() != buf_position) {
505 switch (is_utf8_byte(*buf_position)) {
506 case ByteOfUTF8Classification::EXTENSION_BYTE:
507 no_of_extensions_expected--;
508 if (0 > no_of_extensions_expected) {
510 } else if (0 == no_of_extensions_expected) {
514 case ByteOfUTF8Classification::ONE_BYTE_CHAR:
515 if (0 != no_of_extensions_expected) {
520 case ByteOfUTF8Classification::FIRST_OF_2_BYTES:
521 if (0 != no_of_extensions_expected) {
524 no_of_extensions_expected = 1;
526 case ByteOfUTF8Classification::FIRST_OF_3_BYTES:
527 if (0 != no_of_extensions_expected) {
530 no_of_extensions_expected = 2;
532 case ByteOfUTF8Classification::FIRST_OF_4_BYTES:
533 if (0 != no_of_extensions_expected) {
536 no_of_extensions_expected = 3;
538 case ByteOfUTF8Classification::NOT_VALID:
541 LoggerE("Abnormal value returned from is_utf8_byte function");
549 * add_utf8_chars_to_buffer
550 * Method returns false if byte read is not a valid utf8 char
552 bool add_utf8_chars_to_buffer(FILE* file, std::vector<std::uint8_t>& buf, int chars_to_read,
553 short& extension_bytes_expected) {
555 LoggerD("chars_to_read: %i", chars_to_read);
556 LoggerD("extension_bytes_expected: %i", extension_bytes_expected);
557 while (chars_to_read) {
558 if (extension_bytes_expected) {
559 character = getc(file);
560 if (EOF == character) {
563 buf.push_back(character);
564 if (!--extension_bytes_expected) {
569 character = getc(file);
570 if (EOF == character) {
573 buf.push_back(character);
574 switch (is_utf8_byte(character)) {
575 case ByteOfUTF8Classification::ONE_BYTE_CHAR:
578 case ByteOfUTF8Classification::FIRST_OF_2_BYTES:
579 extension_bytes_expected = 1;
581 case ByteOfUTF8Classification::FIRST_OF_3_BYTES:
582 extension_bytes_expected = 2;
584 case ByteOfUTF8Classification::FIRST_OF_4_BYTES:
585 extension_bytes_expected = 3;
587 case ByteOfUTF8Classification::EXTENSION_BYTE:
588 LoggerD("unexpected EXTENSION_BYTE");
590 case ByteOfUTF8Classification::NOT_VALID:
591 LoggerE("unexpected NOT_VALID byte while reading utf-8");
594 LoggerE("Abnormal. Last read char: %c: ", character);
602 static const char int_to_char[] =
603 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
604 static const std::uint8_t INV = 255;
605 static const std::uint8_t PAD = 254;
606 static const std::uint8_t char_to_int[] = {
607 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
608 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
609 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
610 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
611 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255,
612 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
613 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
614 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
615 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
616 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
617 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
618 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
619 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
620 255, 255, 255, 255, 255, 255, 255, 255, 255};
623 * On error throws std::runtime_error
625 static std::vector<std::uint8_t> decode(const char* str, std::size_t len) {
626 std::vector<std::uint8_t> out;
627 if (len % 4 != 0) throw std::runtime_error("string length is not multiple of 4");
628 if (len == 0) return out;
630 const std::uint8_t* p = (std::uint8_t*)str;
631 const std::uint8_t* end = p + len - 4; // last four can have padding in it
632 std::uint8_t b1, b2, b3, b4;
633 out.reserve(len / 4 * 3);
635 b1 = char_to_int[*p++];
636 b2 = char_to_int[*p++];
637 b3 = char_to_int[*p++];
638 b4 = char_to_int[*p++];
640 if (b1 > 63 || b2 > 63 || b3 > 63 || b4 > 63) throw std::runtime_error("invalid character");
642 out.push_back((b1 << 2) | (b2 >> 4));
643 out.push_back((b2 << 4) | (b3 >> 2));
644 out.push_back((b3 << 6) | b4);
646 b1 = char_to_int[*p++];
647 b2 = char_to_int[*p++];
648 b3 = char_to_int[*p++];
649 b4 = char_to_int[*p++];
651 if (b1 == PAD || b1 == INV || b2 == PAD || b2 == INV || b3 == INV || b4 == INV)
652 throw std::runtime_error("invalid character");
654 out.push_back((b1 << 2) | (b2 >> 4));
656 if (b4 != PAD) throw std::runtime_error("invalid character");
658 out.push_back((b2 << 4) | (b3 >> 2));
659 if (b4 != PAD) out.push_back((b3 << 6) | b4);
664 } // namespace base64
666 void FilesystemInstance::FileReadString(const picojson::value& args, picojson::object& out) {
669 "DEPRECATION WARNING: This method is deprecated since Tizen 5.0.Use FileHandle.readString() "
670 "or FileHandle.readStringNonBlocking() instead.");
672 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
673 CHECK_EXIST(args, "location", out)
674 CHECK_EXIST(args, "offset", out)
676 const std::string& location = args.get("location").get<std::string>();
677 size_t offset = static_cast<size_t>(args.get("offset").get<double>());
678 size_t length = args.contains("length") ? (size_t)args.get("length").get<double>() : NPOS;
679 const std::string& encoding =
680 args.contains("encoding") ? args.get("encoding").get<std::string>() : "utf-8";
683 std::vector<std::uint8_t> buf = read_file(location, offset, length);
685 if (encoding == "iso-8859-1") {
686 out["result"] = picojson::value(picojson::string_type, true);
687 latin1::to_utf8(buf, out["result"].get<std::string>());
689 } else { // default: UTF-8
691 const char* str = (const char*)buf.data();
692 ReportSuccess(picojson::value{str}, out);
694 } catch (std::runtime_error& e) {
695 LoggerE("Cannot read file %s, cause: %s", location.c_str(), e.what());
696 PrepareError(FilesystemError::Other, out);
700 void FilesystemInstance::FileReadBytes(const picojson::value& args, picojson::object& out) {
703 "DEPRECATION WARNING: This method is deprecated since Tizen 5.0. Use FileHandle.readData() "
704 "or FileHandle.readDataNonBlocking() instead.");
706 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
707 CHECK_EXIST(args, "location", out)
708 CHECK_EXIST(args, "offset", out)
709 CHECK_EXIST(args, "length", out)
711 const std::string& location = args.get("location").get<std::string>();
712 size_t offset = static_cast<size_t>(args.get("offset").get<double>());
713 size_t length = static_cast<size_t>(args.get("length").get<double>());
716 std::vector<std::uint8_t> out_data = read_file(location, offset, length);
718 out["result"] = picojson::value(picojson::string_type, true);
719 encode_binary_in_string(out_data, out["result"].get<std::string>());
721 } catch (std::runtime_error& e) {
722 LoggerE("Cannot read file %s, cause: %s", location.c_str(), e.what());
723 PrepareError(FilesystemError::Other, out);
727 void FilesystemInstance::FileWriteString(const picojson::value& args, picojson::object& out) {
730 "DEPRECATION WARNING: FileStream.write() is deprecated since Tizen 5.0. Use "
731 "FileHandle.writeString() or FileHandle.writeStringNonBlocking() instead.");
733 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
734 CHECK_EXIST(args, "location", out)
735 CHECK_EXIST(args, "data", out)
736 CHECK_EXIST(args, "offset", out)
737 CHECK_EXIST(args, "truncate", out)
739 const std::string& location = args.get("location").get<std::string>();
740 const std::string& str = args.get("data").get<std::string>();
741 size_t offset = static_cast<size_t>(args.get("offset").get<double>());
742 bool truncate = static_cast<bool>(args.get("truncate").get<bool>());
743 const char* mode = truncate ? "w" : "r+";
744 const std::string& encoding =
745 args.contains("encoding") ? args.get("encoding").get<std::string>() : "utf-8";
748 if (encoding == "iso-8859-1") {
749 std::vector<std::uint8_t> data;
750 latin1::from_utf8(str, data);
751 write_file(data.data(), data.size(), location, offset, mode);
752 } else { // default: UTF-8
753 const std::uint8_t* buf = (const std::uint8_t*)str.c_str();
754 std::size_t len = str.length();
755 write_file(buf, len, location, offset, mode);
757 } catch (std::runtime_error& e) {
758 LoggerE("Cannot write to file %s, cause: %s", location.c_str(), e.what());
759 PrepareError(FilesystemError::Other, out);
766 void FilesystemInstance::FileWriteBytes(const picojson::value& args, picojson::object& out) {
769 "DEPRECATION WARNING: FileStream.writeBytes() is deprecated since Tizen 5.0. To read and Use "
770 "FileHandle.writeData() or FileHandle.writeDataNonBlocking() instead.");
772 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
773 CHECK_EXIST(args, "location", out)
774 CHECK_EXIST(args, "data", out)
775 CHECK_EXIST(args, "offset", out)
776 CHECK_EXIST(args, "truncate", out)
778 const std::string& location = args.get("location").get<std::string>();
779 const std::string& str = args.get("data").get<std::string>();
780 size_t offset = static_cast<size_t>(args.get("offset").get<double>());
781 bool truncate = static_cast<bool>(args.get("truncate").get<bool>());
782 const char* mode = truncate ? "w" : "r+";
785 std::vector<std::uint8_t> data;
786 decode_binary_from_string(str, data);
787 write_file(data.data(), data.size(), location, offset, mode);
788 } catch (std::runtime_error& e) {
789 LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what());
790 PrepareError(FilesystemError::Other, out);
797 void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson::object& out) {
800 "DEPRECATION WARNING: FileStream.writeBase64() is deprecated since Tizen 5.0. Use "
801 "FileHandle.writeData() or FileHandle.writeDataNonBlocking() in combination with atob() and "
802 "btoa() functions instead.");
804 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
805 CHECK_EXIST(args, "location", out)
806 CHECK_EXIST(args, "data", out)
807 CHECK_EXIST(args, "offset", out)
808 CHECK_EXIST(args, "truncate", out)
810 const std::string& location = args.get("location").get<std::string>();
811 const std::string& str = args.get("data").get<std::string>();
812 size_t offset = static_cast<size_t>(args.get("offset").get<double>());
813 bool truncate = static_cast<bool>(args.get("truncate").get<bool>());
814 const char* mode = truncate ? "w" : "r+";
816 std::vector<std::uint8_t> data;
818 data = base64::decode(str.c_str(), str.length());
819 } catch (std::runtime_error& e) {
820 LoggerE("Can't decode base64: %s", e.what());
821 ReportError(InvalidValuesException(std::string("Can't decode base64: ") + e.what()), out);
826 write_file(data.data(), data.size(), location, offset, mode);
827 ReportSuccess(picojson::value{(double)data.size()}, out);
828 } catch (std::runtime_error& e) {
829 LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what());
830 PrepareError(FilesystemError::Other, out);
834 void FilesystemInstance::FileStat(const picojson::value& args, picojson::object& out) {
836 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
837 CHECK_EXIST(args, "location", out)
838 const std::string& location = args.get("location").get<std::string>();
839 CHECK_STORAGE_ACCESS(location, &out);
841 CHECK_EXIST(args, "callbackId", out)
842 double callback_id = args.get("callbackId").get<double>();
844 auto onSuccess = [this, callback_id](const FilesystemStat& data) {
845 ScopeLogger("Entered into asynchronous function, onSuccess");
846 picojson::value response = picojson::value(picojson::object());
847 picojson::object& obj = response.get<picojson::object>();
848 obj["callbackId"] = picojson::value(callback_id);
849 ReportSuccess(data.toJSON(), obj);
850 Instance::PostMessage(this, response.serialize().c_str());
853 auto onError = [this, callback_id](FilesystemError e) {
854 ScopeLogger("Entered into asynchronous function, onError");
855 picojson::value response = picojson::value(picojson::object());
856 picojson::object& obj = response.get<picojson::object>();
857 obj["callbackId"] = picojson::value(callback_id);
858 PrepareError(e, obj);
859 Instance::PostMessage(this, response.serialize().c_str());
862 FilesystemManager& fsm = FilesystemManager::GetInstance();
863 common::TaskQueue::GetInstance().Async(
864 std::bind(&FilesystemManager::StatPath, &fsm, location, onSuccess, onError));
867 void FilesystemInstance::FileStatSync(const picojson::value& args, picojson::object& out) {
869 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
870 CHECK_EXIST(args, "location", out)
871 const std::string& location = args.get("location").get<std::string>();
872 CHECK_STORAGE_ACCESS(location, &out);
874 auto onSuccess = [&](const FilesystemStat& data) {
875 ScopeLogger("Entered into asynchronous function, onSuccess");
876 ReportSuccess(data.toJSON(), out);
879 auto onError = [&](FilesystemError e) {
880 ScopeLogger("Entered into asynchronous function, onError");
881 PrepareError(e, out);
884 FilesystemManager::GetInstance().StatPath(location, onSuccess, onError);
887 void FilesystemInstance::FilesystemFetchVirtualRoots(const picojson::value& args,
888 picojson::object& out) {
891 auto onSuccess = [&](const std::vector<common::VirtualRoot>& result) {
892 ScopeLogger("Entered into asynchronous function, onSuccess");
893 picojson::array roots;
894 for (const auto& root : result) {
895 roots.push_back(root.ToJson());
897 ReportSuccess(picojson::value(roots), out);
900 auto onError = [&](FilesystemError e) {
901 ScopeLogger("Entered into asynchronous function, onError");
902 PrepareError(e, out);
905 FilesystemManager::GetInstance().GetVirtualRoots(onSuccess, onError);
908 void FilesystemInstance::FileSystemManagerFetchStorages(const picojson::value& args,
909 picojson::object& out) {
912 auto onSuccess = [&](const common::Storages& result) {
913 ScopeLogger("Entered into asynchronous function, onSuccess");
914 picojson::array storages;
915 storages.reserve(result.size());
916 for (const auto storage : result) {
917 storages.push_back(storage->ToJson());
919 ReportSuccess(picojson::value(storages), out);
922 auto onError = [&](FilesystemError e) {
923 ScopeLogger("Entered into asynchronous function, onError");
924 PrepareError(e, out);
927 FilesystemManager::GetInstance().FetchStorages(onSuccess, onError);
929 void FilesystemInstance::StartListening(const picojson::value& args, picojson::object& out) {
931 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
932 FilesystemManager::GetInstance().StartListening();
936 void FilesystemInstance::StopListening(const picojson::value& args, picojson::object& out) {
938 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
939 FilesystemManager::GetInstance().StopListening();
943 void FilesystemInstance::onFilesystemStateChangeSuccessCallback(const common::Storage& storage) {
946 picojson::value event = picojson::value(picojson::object());
947 picojson::object& obj = event.get<picojson::object>();
948 obj["label"] = picojson::value(storage.name_);
949 obj["type"] = picojson::value(common::Storage::ToString(storage.type()));
950 obj["state"] = picojson::value(common::Storage::ToString(storage.state()));
951 obj["listenerId"] = picojson::value("StorageStateChangeListener");
952 Instance::PostMessage(this, event.serialize().c_str());
955 void FilesystemInstance::onFilesystemStateChangeErrorCallback() {
957 picojson::value event = picojson::value(picojson::object());
958 picojson::object& obj = event.get<picojson::object>();
959 LogAndReportError(UnknownException(std::string("Failed to registerd listener")), obj);
960 obj["listenerId"] = picojson::value("StorageStateChangeListener");
961 LoggerD("Posting: %s", event.serialize().c_str());
962 Instance::PostMessage(this, event.serialize().c_str());
965 void FilesystemInstance::FileSystemManagerMakeDirectory(const picojson::value& args,
966 picojson::object& out) {
968 CHECK_EXIST(args, "callbackId", out)
969 CHECK_EXIST(args, "location", out)
971 double callback_id = args.get("callbackId").get<double>();
972 const std::string& location = args.get("location").get<std::string>();
974 auto onResult = [this, callback_id](FilesystemError e) {
975 ScopeLogger("Entered into asynchronous function, onResult");
976 picojson::value response = picojson::value(picojson::object());
977 picojson::object& obj = response.get<picojson::object>();
978 obj["callbackId"] = picojson::value(callback_id);
979 if (e == FilesystemError::DirectoryExists)
982 PrepareError(e, obj);
983 Instance::PostMessage(this, response.serialize().c_str());
986 auto onAction = [location, onResult]() {
987 ScopeLogger("Entered into asynchronous function, onAction");
988 FilesystemManager::GetInstance().MakeDirectory(location, onResult);
991 common::TaskQueue::GetInstance().Async(onAction);
994 void FilesystemInstance::FileSystemManagerMakeDirectorySync(const picojson::value& args,
995 picojson::object& out) {
998 "DEPRECATION WARNING: File.createDirectory() is deprecated since Tizen 5.0. Use "
999 "FileSystemManager.createDirectory() instead.");
1001 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1002 CHECK_EXIST(args, "location", out)
1003 const std::string& location = args.get("location").get<std::string>();
1004 CHECK_STORAGE_ACCESS(location, &out);
1006 auto onResult = [&](FilesystemError e) {
1007 ScopeLogger("Entered into asynchronous function, onResult");
1008 if (e == FilesystemError::DirectoryExists)
1011 PrepareError(e, out);
1014 FilesystemManager::GetInstance().MakeDirectory(location, onResult);
1017 void FilesystemInstance::ReadDir(const picojson::value& args, picojson::object& out) {
1020 "DEPRECATION WARNING: File.listFiles() is deprecated since Tizen 5.0. Use "
1021 "FileSystemManager.listDirectory() instead.");
1023 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1024 CHECK_EXIST(args, "pathToDir", out)
1025 CHECK_EXIST(args, "callbackId", out)
1027 double callback_id = args.get("callbackId").get<double>();
1028 const std::string& pathToDir = args.get("pathToDir").get<std::string>();
1030 auto onSuccess = [this, callback_id](const std::vector<std::string>& paths) {
1031 ScopeLogger("Entered into asynchronous function, onSuccess");
1032 picojson::value result = picojson::value(picojson::array());
1034 picojson::array& statPaths = result.get<picojson::array>();
1035 picojson::value response = picojson::value(picojson::object());
1036 picojson::object& obj = response.get<picojson::object>();
1037 obj["callbackId"] = picojson::value(callback_id);
1038 for (auto path : paths) {
1039 FilesystemStat stat = FilesystemStat::getStat(path);
1040 statPaths.push_back(stat.toJSON());
1042 ReportSuccess(result, obj);
1043 Instance::PostMessage(this, response.serialize().c_str());
1046 auto onError = [this, callback_id](FilesystemError e) {
1047 ScopeLogger("Entered into asynchronous function, onError");
1048 picojson::value response = picojson::value(picojson::object());
1049 picojson::object& obj = response.get<picojson::object>();
1050 obj["callbackId"] = picojson::value(callback_id);
1051 PrepareError(e, obj);
1052 Instance::PostMessage(this, response.serialize().c_str());
1055 FilesystemManager& fm = FilesystemManager::GetInstance();
1056 common::TaskQueue::GetInstance().Async(
1057 std::bind(&FilesystemManager::ReadDir, &fm, pathToDir, onSuccess, onError));
1060 void FilesystemInstance::UnlinkFile(const picojson::value& args, picojson::object& out) {
1063 "DEPRECATION WARNING: File.deleteFile() is deprecated since Tizen 5.0. Use "
1064 "FileSystemManager.deleteFile() instead.");
1066 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1067 CHECK_EXIST(args, "pathToFile", out)
1068 double callback_id = args.get("callbackId").get<double>();
1069 const std::string& pathToFile = args.get("pathToFile").get<std::string>();
1070 CHECK_STORAGE_ACCESS(pathToFile, &out);
1072 auto onSuccess = [this, callback_id]() {
1073 ScopeLogger("Entered into asynchronous function, onSuccess");
1074 picojson::value result = picojson::value();
1075 picojson::value response = picojson::value(picojson::object());
1076 picojson::object& obj = response.get<picojson::object>();
1077 obj["callbackId"] = picojson::value(callback_id);
1078 ReportSuccess(result, obj);
1079 Instance::PostMessage(this, response.serialize().c_str());
1082 auto onError = [this, callback_id](FilesystemError e) {
1083 ScopeLogger("Entered into asynchronous function, onError");
1084 picojson::value response = picojson::value(picojson::object());
1085 picojson::object& obj = response.get<picojson::object>();
1086 obj["callbackId"] = picojson::value(callback_id);
1087 PrepareError(e, obj);
1088 Instance::PostMessage(this, response.serialize().c_str());
1091 FilesystemManager& fm = FilesystemManager::GetInstance();
1092 common::TaskQueue::GetInstance().Async(
1093 std::bind(&FilesystemManager::UnlinkFile, &fm, pathToFile, onSuccess, onError));
1096 void FilesystemInstance::RemoveDirectory(const picojson::value& args, picojson::object& out) {
1099 "DEPRECATION WARNING: File.deleteDirectory() is deprecated since Tizen 5.0. Use "
1100 "FileSystemManager.deleteDirectory() instead.");
1102 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1103 CHECK_EXIST(args, "pathToDelete", out)
1104 double callback_id = args.get("callbackId").get<double>();
1105 const std::string& pathToDelete = args.get("pathToDelete").get<std::string>();
1106 CHECK_STORAGE_ACCESS(pathToDelete, &out);
1108 auto onSuccess = [this, callback_id]() {
1109 ScopeLogger("Entered into asynchronous function, onSuccess");
1110 picojson::value result = picojson::value();
1111 picojson::value response = picojson::value(picojson::object());
1112 picojson::object& obj = response.get<picojson::object>();
1113 obj["callbackId"] = picojson::value(callback_id);
1114 ReportSuccess(result, obj);
1115 Instance::PostMessage(this, response.serialize().c_str());
1118 auto onError = [this, callback_id](FilesystemError e) {
1119 ScopeLogger("Entered into asynchronous function, onError");
1120 picojson::value response = picojson::value(picojson::object());
1121 picojson::object& obj = response.get<picojson::object>();
1122 obj["callbackId"] = picojson::value(callback_id);
1123 PrepareError(e, obj);
1124 Instance::PostMessage(this, response.serialize().c_str());
1127 FilesystemManager& fm = FilesystemManager::GetInstance();
1128 common::TaskQueue::GetInstance().Async(
1129 std::bind(&FilesystemManager::RemoveDirectory, &fm, pathToDelete, onSuccess, onError));
1132 void FilesystemInstance::CopyTo(const picojson::value& args, picojson::object& out) {
1135 "DEPRECATION WARNING: File.copyTo() is deprecated since Tizen 5.0. Use "
1136 "FileSystemManager.CopyFile() or FileSystemManager.CopyDirectory() instead.");
1138 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1139 CHECK_EXIST(args, "callbackId", out)
1141 CHECK_EXIST(args, "originFilePath", out)
1142 const std::string& originPath = args.get("originFilePath").get<std::string>();
1143 CHECK_STORAGE_ACCESS(originPath, &out);
1144 CHECK_EXIST(args, "destinationFilePath", out)
1145 const std::string& destinationPath = args.get("destinationFilePath").get<std::string>();
1146 CHECK_STORAGE_ACCESS(destinationPath, &out);
1148 CHECK_EXIST(args, "overwrite", out)
1150 double callback_id = args.get("callbackId").get<double>();
1151 const bool& overwrite = args.get("overwrite").get<bool>();
1153 auto onSuccess = [this, callback_id]() {
1154 ScopeLogger("Entered into asynchronous function, onSuccess");
1155 picojson::value result = picojson::value();
1156 picojson::value response = picojson::value(picojson::object());
1157 picojson::object& obj = response.get<picojson::object>();
1158 obj["callbackId"] = picojson::value(callback_id);
1159 ReportSuccess(result, obj);
1160 Instance::PostMessage(this, response.serialize().c_str());
1163 auto onError = [this, callback_id](FilesystemError e) {
1164 ScopeLogger("Entered into asynchronous function, onError");
1165 picojson::value response = picojson::value(picojson::object());
1166 picojson::object& obj = response.get<picojson::object>();
1167 obj["callbackId"] = picojson::value(callback_id);
1168 PrepareError(e, obj);
1169 Instance::PostMessage(this, response.serialize().c_str());
1172 FilesystemManager& fm = FilesystemManager::GetInstance();
1173 common::TaskQueue::GetInstance().Async(std::bind(&FilesystemManager::CopyTo, &fm, originPath,
1174 destinationPath, overwrite, onSuccess, onError));
1177 void FilesystemInstance::PrepareError(const FilesystemError& error, picojson::object& out) {
1180 case FilesystemError::None:
1181 LogAndReportError(UnknownException("PLATFORM ERROR"), out);
1183 case FilesystemError::NotFound:
1184 LogAndReportError(NotFoundException("PLATFORM ERROR"), out,
1185 ("NotFoundException - PLATFORM ERROR"));
1187 case FilesystemError::FileExists:
1188 LogAndReportError(IOException("File already exists"), out,
1189 ("IOException - File already exists"));
1191 case FilesystemError::DirectoryExists:
1192 LogAndReportError(IOException("Directory already exists"), out,
1193 ("IOException - Directory already exists"));
1195 case FilesystemError::PermissionDenied:
1196 LogAndReportError(IOException("Permission denied"), out, ("IOException - Permission denied"));
1198 case FilesystemError::IOError:
1199 LogAndReportError(IOException("IO Error"), out, ("IOException - IO Error"));
1201 case FilesystemError::Other:
1202 LogAndReportError(UnknownException("PLATFORM ERROR other"), out,
1203 ("UnknownException - PLATFORM ERROR other"));
1205 case FilesystemError::InvalidValue:
1206 LogAndReportError(InvalidValuesException("PLATFORM ERROR"), out,
1207 ("InvalidValuesException - PLATFORM ERROR"));
1210 LogAndReportError(UnknownException("PLATFORM ERROR default"), out,
1211 ("UnknownException - PLATFORM ERROR default"));
1216 void FilesystemInstance::FileSystemManagerGetCanonicalPath(const picojson::value& args,
1217 picojson::object& out) {
1219 // TODO: any privilege needed?
1220 CHECK_EXIST(args, "path", out);
1222 const std::string& path = args.get("path").get<std::string>();
1224 auto onSuccess = [&](const std::string& canonicalPath) {
1225 ScopeLogger("Entered into asynchronous function, onSuccess");
1226 ReportSuccess(picojson::value(canonicalPath), out);
1229 auto onError = [&](FilesystemError e) {
1230 ScopeLogger("Entered into asynchronous function, onError");
1231 PrepareError(e, out);
1234 FilesystemManager::GetInstance().GetCanonicalPath(path, onSuccess, onError);
1239 FILE* OpenFile(const std::string& path, const std::string& fopen_mode) {
1240 FILE* file = std::fopen(path.c_str(), fopen_mode.c_str());
1242 throw std::system_error{errno, std::generic_category(), "Could not open file"};
1248 FILE* MakeParentsAndOpenFile(const std::string& path, const std::string& fopen_mode) {
1250 * If fopen fails, created parent directories have to be removed.
1251 * Save the path to the first nonexistent parent directory in the file path,
1252 * to know where to start recursive removal
1254 std::string first_nonexistent_parent = FilesystemUtils::Dirname(path);
1258 !FilesystemUtils::CheckIfExists(FilesystemUtils::Dirname(first_nonexistent_parent), &buf)) {
1259 first_nonexistent_parent = FilesystemUtils::Dirname(first_nonexistent_parent);
1262 FilesystemUtils::Mkdir(FilesystemUtils::Dirname(path), true);
1264 FILE* file = std::fopen(path.c_str(), fopen_mode.c_str());
1269 std::system_error fopen_error =
1270 std::system_error(errno, std::generic_category(), "Could not open file");
1273 FilesystemUtils::RemoveDirectoryRecursively(first_nonexistent_parent);
1274 } catch (const std::system_error& error) {
1275 LoggerD("Failed to remove created parent directories: %s", error.what());
1283 void FilesystemInstance::FileSystemManagerOpenFile(const picojson::value& args,
1284 picojson::object& out) {
1286 const int unique_id = static_cast<int>(args.get("id").get<double>());
1287 if (opened_files.find(unique_id) != opened_files.end()) {
1288 LogAndReportError(IOException("Internal error (id is already in use)"), out);
1292 bool access_checked = false;
1293 if (WriteAccessRequested(args)) {
1294 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1295 access_checked = true;
1298 if (ReadAccessRequested(args)) {
1299 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1300 access_checked = true;
1303 // File open mode received from JS layer can be different than expected by
1304 // WriteAccessRequested and ReadAccessRequested functions. In case like that
1305 // privilege would not be checked and user could gain unauthorized access to file.
1306 // To prevent this situation we only accept specific open modes.
1307 if (false == access_checked) {
1308 const std::string& open_mode = args.get("openMode").get<std::string>();
1309 LogAndReportError(TypeMismatchException("Invalid open mode: " + open_mode), out);
1313 const std::string& path = args.get("path").get<std::string>();
1315 CHECK_STORAGE_ACCESS(path, &out);
1316 const std::string open_mode = GetFopenMode(args);
1317 FILE* file = nullptr;
1319 if (ShouldMakeParents(args)) {
1320 file = MakeParentsAndOpenFile(path, open_mode);
1322 file = OpenFile(path, open_mode);
1324 } catch (const std::system_error& error) {
1325 FilesystemUtils::TranslateException(error, out);
1329 opened_files.emplace(std::make_pair(unique_id, std::make_shared<FileHandle>(file)));
1333 void FilesystemInstance::FileSystemManagerCreateDirectory(const picojson::value& args,
1334 picojson::object& out) {
1336 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1338 double callback_id = args.get("callbackId").get<double>();
1339 const std::string& path = args.get("path").get<std::string>();
1340 bool make_parents = args.get("makeParents").get<bool>();
1341 CHECK_STORAGE_ACCESS(path, &out);
1343 this->worker.add_job([this, callback_id, path, make_parents] {
1344 picojson::value response = picojson::value(picojson::object());
1345 picojson::object& obj = response.get<picojson::object>();
1346 obj["callbackId"] = picojson::value(callback_id);
1349 FilesystemUtils::Mkdir(path, make_parents);
1350 ReportSuccess(picojson::value(path), obj);
1351 } catch (const std::system_error& e) {
1352 FilesystemUtils::TranslateException(e, obj);
1354 this->PostMessage(response.serialize().c_str());
1360 void FilesystemInstance::FileSystemManagerDeleteFile(const picojson::value& args,
1361 picojson::object& out) {
1363 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1365 double callback_id = args.get("callbackId").get<double>();
1366 const std::string& path = args.get("path").get<std::string>();
1368 CHECK_STORAGE_ACCESS(path, &out);
1369 this->worker.add_job([this, callback_id, path] {
1370 picojson::value response = picojson::value(picojson::object());
1371 picojson::object& obj = response.get<picojson::object>();
1372 obj["callbackId"] = picojson::value(callback_id);
1374 this->PostMessage(response.serialize().c_str());
1379 if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) {
1380 LogAndReportError(NotFoundException("Given path does not point to file."), obj);
1383 FilesystemUtils::Unlink(path);
1384 ReportSuccess(picojson::value(FilesystemUtils::Dirname(path)), obj);
1385 } catch (const std::system_error& e) {
1386 FilesystemUtils::TranslateException(e, obj);
1393 void FilesystemInstance::FileSystemManagerDeleteDirectory(const picojson::value& args,
1394 picojson::object& out) {
1396 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1398 double callback_id = args.get("callbackId").get<double>();
1399 const std::string& path = args.get("path").get<std::string>();
1401 CHECK_STORAGE_ACCESS(path, &out);
1402 bool recursive = args.get("recursive").get<bool>();
1404 this->worker.add_job([this, callback_id, path, recursive] {
1406 picojson::value response = picojson::value(picojson::object());
1407 picojson::object& obj = response.get<picojson::object>();
1408 obj["callbackId"] = picojson::value(callback_id);
1410 this->PostMessage(response.serialize().c_str());
1415 if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) {
1416 LogAndReportError(NotFoundException("Given path does not point to directory."), obj);
1420 FilesystemUtils::RemoveDirectoryRecursively(path);
1422 FilesystemUtils::RemoveDirectory(path);
1424 ReportSuccess(picojson::value(FilesystemUtils::Dirname(path)), obj);
1425 } catch (const std::system_error& e) {
1426 FilesystemUtils::TranslateException(e, obj);
1433 void FilesystemInstance::FileSystemManagerCopyFile(const picojson::value& args,
1434 picojson::object& out) {
1436 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1437 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1439 double callback_id = args.get("callbackId").get<double>();
1440 const std::string& path = args.get("path").get<std::string>();
1441 CHECK_STORAGE_ACCESS(path, &out);
1442 const std::string& destination_path = args.get("destinationPath").get<std::string>();
1443 CHECK_STORAGE_ACCESS(destination_path, &out);
1444 bool overwrite = args.get("overwrite").get<bool>();
1446 this->worker.add_job([this, callback_id, path, destination_path, overwrite] {
1447 picojson::value response = picojson::value(picojson::object());
1448 picojson::object& obj = response.get<picojson::object>();
1449 obj["callbackId"] = picojson::value(callback_id);
1451 this->PostMessage(response.serialize().c_str());
1456 if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) {
1457 LogAndReportError(NotFoundException("Given path does not point to file."), obj);
1461 if (FilesystemUtils::CheckIfExists(destination_path, &buf) && !overwrite) {
1463 IOException("Given path points to an existing resource, overwriting is not allowed."),
1467 FilesystemUtils::CopyFile(path, destination_path, overwrite);
1468 ReportSuccess(picojson::value(destination_path), obj);
1469 } catch (const std::system_error& e) {
1470 FilesystemUtils::TranslateException(e, obj);
1477 void FilesystemInstance::FileSystemManagerCopyDirectory(const picojson::value& args,
1478 picojson::object& out) {
1480 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1481 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1482 const std::string& path = args.get("path").get<std::string>();
1483 CHECK_STORAGE_ACCESS(path, &out);
1484 const std::string& destination_path = args.get("destinationPath").get<std::string>();
1485 CHECK_STORAGE_ACCESS(destination_path, &out);
1486 double callback_id = args.get("callbackId").get<double>();
1487 bool overwrite = args.get("overwrite").get<bool>();
1489 this->worker.add_job([this, callback_id, path, destination_path, overwrite] {
1491 picojson::value response = picojson::value(picojson::object());
1492 picojson::object& obj = response.get<picojson::object>();
1493 obj["callbackId"] = picojson::value(callback_id);
1495 this->PostMessage(response.serialize().c_str());
1500 if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) {
1501 LogAndReportError(NotFoundException("Given path does not point to directory."), obj);
1505 bool exists = FilesystemUtils::CheckIfExists(destination_path, &buf);
1506 if (exists && !FilesystemUtils::CheckIfDir(buf)) {
1507 LogAndReportError(IOException("File with conflicting name already exists."), obj);
1509 } else if (!exists) {
1510 FilesystemUtils::Mkdir(destination_path);
1512 FilesystemUtils::CopyDirectory(path, destination_path, overwrite);
1513 ReportSuccess(picojson::value(destination_path), obj);
1514 } catch (const std::system_error& e) {
1515 FilesystemUtils::TranslateException(e, obj);
1522 void FilesystemInstance::FileSystemManagerMoveFile(const picojson::value& args,
1523 picojson::object& out) {
1525 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1526 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1528 const std::string& path = args.get("path").get<std::string>();
1529 CHECK_STORAGE_ACCESS(path, &out);
1530 const std::string& destination_path = args.get("destinationPath").get<std::string>();
1531 CHECK_STORAGE_ACCESS(destination_path, &out);
1532 double callback_id = args.get("callbackId").get<double>();
1533 bool overwrite = args.get("overwrite").get<bool>();
1535 this->worker.add_job([this, callback_id, path, destination_path, overwrite] {
1537 picojson::value response = picojson::value(picojson::object());
1538 picojson::object& obj = response.get<picojson::object>();
1539 obj["callbackId"] = picojson::value(callback_id);
1541 this->PostMessage(response.serialize().c_str());
1546 if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) {
1547 LogAndReportError(NotFoundException("Given path does not point to file."), obj);
1551 if (!FilesystemUtils::CheckIfExists(destination_path, &buf) ||
1552 !FilesystemUtils::CheckIfDir(buf)) {
1553 LogAndReportError(NotFoundException("Given path does not point to directory."), obj);
1558 std::string new_path = destination_path + '/' + FilesystemUtils::PosixBasename(path);
1559 if (!overwrite && FilesystemUtils::CheckIfExists(new_path, &buf)) {
1560 LogAndReportError(IOException("File or directory with conflicting name already exists."),
1564 FilesystemUtils::MoveFile(path, new_path, overwrite);
1565 ReportSuccess(picojson::value(new_path), obj);
1566 } catch (const std::system_error& e) {
1567 FilesystemUtils::TranslateException(e, obj);
1574 void FilesystemInstance::FileSystemManagerMoveDirectory(const picojson::value& args,
1575 picojson::object& out) {
1578 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1579 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1580 double callback_id = args.get("callbackId").get<double>();
1581 const std::string& path = args.get("path").get<std::string>();
1582 CHECK_STORAGE_ACCESS(path, &out);
1583 const std::string& destination_path = args.get("destinationPath").get<std::string>();
1584 CHECK_STORAGE_ACCESS(destination_path, &out);
1585 bool overwrite = args.get("overwrite").get<bool>();
1587 this->worker.add_job([this, callback_id, path, destination_path, overwrite] {
1588 picojson::value response = picojson::value(picojson::object());
1589 picojson::object& obj = response.get<picojson::object>();
1590 obj["callbackId"] = picojson::value(callback_id);
1592 this->PostMessage(response.serialize().c_str());
1597 if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) {
1598 LogAndReportError(NotFoundException("Given path does not point to directory."), obj);
1602 if (!FilesystemUtils::CheckIfExists(destination_path, &buf) ||
1603 !FilesystemUtils::CheckIfDir(buf)) {
1604 LogAndReportError(NotFoundException("Given path does not point to directory."), obj);
1608 std::string new_path = destination_path + '/' + FilesystemUtils::PosixBasename(path);
1609 if (FilesystemUtils::CheckIfExists(new_path, &buf) && !FilesystemUtils::CheckIfDir(buf)) {
1610 LogAndReportError(IOException("File or directory with conflicting name already exists."),
1614 FilesystemUtils::MoveDirectory(path, destination_path, overwrite);
1615 ReportSuccess(picojson::value(new_path), obj);
1616 } catch (const std::system_error& e) {
1617 FilesystemUtils::TranslateException(e, obj);
1624 void FilesystemInstance::FileSystemManagerRename(const picojson::value& args,
1625 picojson::object& out) {
1627 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1628 const std::string& path = args.get("path").get<std::string>();
1630 CHECK_STORAGE_ACCESS(path, &out);
1631 double callback_id = args.get("callbackId").get<double>();
1632 const std::string& new_name = args.get("newName").get<std::string>();
1634 this->worker.add_job([this, callback_id, new_name, path] {
1636 picojson::value response = picojson::value(picojson::object());
1637 picojson::object& obj = response.get<picojson::object>();
1638 obj["callbackId"] = picojson::value(callback_id);
1640 this->PostMessage(response.serialize().c_str());
1645 bool exists = FilesystemUtils::CheckIfExists(path, &buf);
1647 LogAndReportError(NotFoundException("Given path does not point to file or directory."),
1651 std::string new_path{FilesystemUtils::Dirname(path) + "/" + new_name};
1653 exists = FilesystemUtils::CheckIfExists(new_path, &buf);
1655 LogAndReportError(IOException("File or directory with conflicting name already exists"),
1659 FilesystemUtils::Rename(path, new_path);
1660 ReportSuccess(picojson::value(new_path), obj);
1661 } catch (const std::system_error& e) {
1662 FilesystemUtils::TranslateException(e, obj);
1669 void FilterResult(std::vector<const char*>& names, std::vector<unsigned char>& types, bool is_type,
1670 unsigned char type) {
1671 int i = (int)names.size() - 1;
1674 if (is_type ? type != types[i] : type == types[i]) {
1675 names.erase(names.begin() + i);
1681 void FilesystemInstance::FileSystemManagerListDirectory(const picojson::value& args,
1682 picojson::object& out) {
1684 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1686 double callback_id = args.get("callbackId").get<double>();
1687 const std::string& path = args.get("path").get<std::string>();
1688 const picojson::object& filter = args.get("filter").get<picojson::object>();
1689 CHECK_STORAGE_ACCESS(path, &out);
1691 this->worker.add_job([this, callback_id, path, filter] {
1693 picojson::value response = picojson::value(picojson::object());
1694 picojson::object& obj = response.get<picojson::object>();
1695 obj["callbackId"] = picojson::value(callback_id);
1698 std::vector<const char*> names;
1700 std::vector<unsigned char> types;
1701 FilesystemUtils::ListDirectory(path, [&](const char* name, unsigned char type) {
1702 names.push_back(name);
1703 types.push_back(type);
1706 auto it = filter.find("isFile");
1707 if (filter.end() != it) {
1708 FilterResult(names, types, it->second.get<bool>(), DT_REG);
1711 it = filter.find("isDirectory");
1712 if (filter.end() != it) {
1713 FilterResult(names, types, it->second.get<bool>(), DT_DIR);
1717 auto start_modified_it = filter.find("startModified"),
1718 end_modified_it = filter.find("endModified"),
1719 start_created_it = filter.find("startCreated"),
1720 end_created_it = filter.find("endCreated");
1721 if (filter.end() != start_modified_it || filter.end() != end_modified_it ||
1722 filter.end() != start_created_it || filter.end() != end_created_it) {
1723 auto name_iterator = names.begin();
1724 while (name_iterator != names.end()) {
1726 std::string path_with_name = path + std::string("/") + std::string(*name_iterator);
1727 int status = ::stat(path_with_name.c_str(), &buf);
1729 throw std::system_error{errno, std::generic_category(),
1730 "Failed to get last modification date of a file"};
1732 if (filter.end() != start_modified_it &&
1733 (buf.st_mtime < start_modified_it->second.get<double>())) {
1734 name_iterator = names.erase(name_iterator);
1737 if (filter.end() != end_modified_it &&
1738 (buf.st_mtime > end_modified_it->second.get<double>())) {
1739 name_iterator = names.erase(name_iterator);
1742 if (filter.end() != start_created_it &&
1743 (buf.st_ctime < start_created_it->second.get<double>())) {
1744 name_iterator = names.erase(name_iterator);
1747 if (filter.end() != end_created_it &&
1748 (buf.st_ctime > end_created_it->second.get<double>())) {
1749 name_iterator = names.erase(name_iterator);
1756 picojson::value value = picojson::value(picojson::object());
1757 picojson::object& object = value.get<picojson::object>();
1759 object["names"] = picojson::value{picojson::array_type, true};
1760 object["path"] = picojson::value(path);
1762 picojson::array& names_array = object["names"].get<picojson::array>();
1763 names_array.reserve(names.size());
1764 for (unsigned int i = 0; i < names.size(); ++i) {
1765 names_array.push_back(picojson::value(names[i]));
1768 ReportSuccess(value, obj);
1769 } catch (const std::system_error& e) {
1770 FilesystemUtils::TranslateException(e, obj);
1772 this->PostMessage(response.serialize().c_str());
1778 void FilesystemInstance::FileSystemManagerIsFile(const picojson::value& args,
1779 picojson::object& out) {
1781 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1782 const std::string& path = args.get("path").get<std::string>();
1784 CHECK_STORAGE_ACCESS(path, &out);
1785 picojson::value is_file{};
1788 bool exists = FilesystemUtils::CheckIfExists(path, &buf);
1790 LogAndReportError(NotFoundException("Given path does not point to file."), out);
1793 is_file = picojson::value{FilesystemUtils::CheckIfFile(buf)};
1794 } catch (const std::system_error& e) {
1795 FilesystemUtils::TranslateException(e, out);
1797 ReportSuccess(is_file, out);
1800 void FilesystemInstance::FileSystemManagerIsDirectory(const picojson::value& args,
1801 picojson::object& out) {
1803 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1804 const std::string& path = args.get("path").get<std::string>();
1806 CHECK_STORAGE_ACCESS(path, &out);
1807 picojson::value is_directory{};
1810 bool exists = FilesystemUtils::CheckIfExists(path, &buf);
1812 LogAndReportError(NotFoundException("Given path does not point to directory."), out);
1815 is_directory = picojson::value{FilesystemUtils::CheckIfDir(buf)};
1816 } catch (const std::system_error& e) {
1817 FilesystemUtils::TranslateException(e, out);
1819 ReportSuccess(is_directory, out);
1822 void FilesystemInstance::FileSystemManagerPathExists(const picojson::value& args,
1823 picojson::object& out) {
1825 CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1826 const std::string& path = args.get("path").get<std::string>();
1828 CHECK_STORAGE_ACCESS(path, &out);
1829 picojson::value does_file_exist = picojson::value{true};
1832 bool exists = FilesystemUtils::CheckIfExists(path, &buf);
1834 does_file_exist = picojson::value{false};
1836 } catch (const std::system_error& e) {
1837 FilesystemUtils::TranslateException(e, out);
1839 ReportSuccess(does_file_exist, out);
1842 void FilesystemInstance::FileSystemManagerGetLimits(const picojson::value& args,
1843 picojson::object& out) {
1845 picojson::value response =
1846 picojson::value{picojson::array{picojson::value{static_cast<double>(NAME_MAX)},
1847 picojson::value{static_cast<double>(PATH_MAX)}}};
1848 ReportSuccess(response, out);
1851 void FilesystemInstance::FileHandleSeek(const picojson::value& args, picojson::object& out) {
1853 const int fh_id = static_cast<int>(args.get("id").get<double>());
1854 const long offset = static_cast<long>(args.get("offset").get<double>());
1855 const std::string& _whence = args.get("whence").get<std::string>();
1857 auto fh = opened_files.find(fh_id);
1859 int whence = SEEK_SET;
1861 if ("CURRENT" == _whence) {
1863 LoggerD("SEEK_CUR selected");
1864 } else if ("END" == _whence) {
1866 LoggerD("SEEK_END selected");
1869 if (opened_files.end() == fh) {
1870 LogAndReportError(IOException("Invalid FileHandle"), out);
1874 auto handle = fh->second;
1876 auto logic = [handle, whence, offset](decltype(out) out) {
1877 long ret = fseek(handle->file_handle, offset, whence);
1879 LoggerE("fseek returned failed");
1880 std::string error_message =
1881 std::string("seek failed, fileHandle may be corrupted, error message: ") +
1882 GetErrorString(errno);
1883 LogAndReportError(IOException(error_message.c_str()), out);
1887 ret = ftell(handle->file_handle);
1889 LoggerE("ftell returned failed");
1890 std::string error_message =
1891 std::string("seek failed, fileHandle may be corrupted, error message: ") +
1892 GetErrorString(errno);
1893 LogAndReportError(IOException(error_message.c_str()), out);
1897 ReportSuccess(picojson::value((double)ret), out);
1900 bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
1906 double callback_id = args.get("callbackId").get<double>();
1907 this->worker.add_job([this, callback_id, logic] {
1908 picojson::value response = picojson::value(picojson::object());
1909 picojson::object& async_out = response.get<picojson::object>();
1910 async_out["callbackId"] = picojson::value(callback_id);
1912 this->PostMessage(response.serialize().c_str());
1920 void FilesystemInstance::FileHandleReadString(const picojson::value& args, picojson::object& out) {
1922 CHECK_EXIST(args, "id", out)
1923 const int fh_id = static_cast<int>(args.get("id").get<double>());
1924 const std::string& encoding =
1925 args.contains("encoding") ? args.get("encoding").get<std::string>() : "UTF-8";
1926 if (encoding != kISOEncoding && encoding != kUTF8Encoding) {
1927 LogAndReportError(NotSupportedException("Given encoding is not supported."), out);
1931 auto fh = opened_files.find(fh_id);
1932 if (opened_files.end() == fh) {
1933 LogAndReportError(IOException("Invalid FileHandle"), out);
1938 bool whole_file = false;
1939 if (args.contains("count")) {
1940 // If user passed 'count' parameter, we need to read at most 'count' characters.
1941 double count_double = args.get("count").get<double>();
1942 if (std::string::npos <= static_cast<unsigned long long>(count_double)) {
1943 LogAndReportError(InvalidValuesException("Invalid count was given"), out);
1946 count = static_cast<size_t>(count_double);
1949 count = file_size(fh->second->file_handle);
1951 } catch (const std::system_error& e) {
1952 LogAndReportError(IOException(e.what()), out);
1956 LoggerD("count: %zu", count);
1958 auto handle = fh->second;
1960 auto logic = [handle, count, encoding, whole_file](decltype(out) out) {
1962 size_t read_bytes = 0;
1963 std::vector<std::uint8_t> buf = read_file(handle->file_handle, count, &read_bytes);
1964 buf.resize(read_bytes); // this protects from reporting too big arrays to JS
1965 if (encoding == kISOEncoding) { // for iso-8859-1 1 byte is equal to 1 character
1966 out["result"] = picojson::value(picojson::string_type, true);
1967 latin1::to_utf8(buf, out["result"].get<std::string>());
1970 unsigned long char_count;
1971 short expected_extension_bytes;
1972 if (!validate_and_check_character_count(buf, char_count, expected_extension_bytes)) {
1974 IOException("File doesn't contain UTF-8 encoded string with given length"), out);
1977 LoggerD("char_count: %lu", char_count);
1978 LoggerD("ftell: %ld", ftell(handle->file_handle));
1980 handle->file_handle))) { // read number of characters if not whole file read
1981 LoggerD("count parameter given: %zu", count);
1983 !add_utf8_chars_to_buffer(handle->file_handle, buf, count - char_count,
1984 expected_extension_bytes)) {
1986 IOException("File doesn't contain UTF-8 encoded string with given length"), out);
1989 const char* str = (const char*)buf.data();
1990 ReportSuccess(picojson::value{str, buf.size()}, out);
1992 } catch (std::runtime_error& e) {
1993 LoggerE("Cannot read, cause: %s", e.what());
1994 LogAndReportError(IOException(e.what()), out);
1998 bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2004 double callback_id = args.get("callbackId").get<double>();
2005 this->worker.add_job([this, callback_id, logic] {
2006 picojson::value response = picojson::value(picojson::object());
2007 picojson::object& async_out = response.get<picojson::object>();
2008 async_out["callbackId"] = picojson::value(callback_id);
2010 this->PostMessage(response.serialize().c_str());
2018 void FilesystemInstance::FileHandleWriteString(const picojson::value& args, picojson::object& out) {
2020 CHECK_EXIST(args, "id", out)
2021 CHECK_EXIST(args, "string", out)
2022 const int fh_id = static_cast<int>(args.get("id").get<double>());
2023 const std::string& str = args.get("string").get<std::string>();
2024 const std::string& encoding =
2025 args.contains("encoding") ? args.get("encoding").get<std::string>() : "UTF-8";
2026 if (encoding != kISOEncoding && encoding != kUTF8Encoding) {
2027 LogAndReportError(NotSupportedException("Given encoding is not supported."), out);
2031 auto fh = opened_files.find(fh_id);
2032 if (opened_files.end() == fh) {
2033 LogAndReportError(IOException("Invalid FileHandle"), out);
2037 auto handle = fh->second;
2039 auto logic = [str, handle, encoding](decltype(out) out) {
2041 std::vector<std::uint8_t> data;
2042 data.resize(str.size());
2044 if (encoding == kISOEncoding) {
2045 latin1::from_utf8(str, data);
2047 LoggerD("copying string memory to vector");
2048 std::memcpy(data.data(), str.data(), str.size());
2050 write_file(data.data(), data.size(), handle->file_handle);
2051 ReportSuccess(picojson::value{(double)data.size()}, out);
2052 } catch (std::runtime_error& e) {
2053 LoggerE("Cannot write, cause: %s", e.what());
2054 LogAndReportError(IOException(e.what()), out);
2058 bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2064 double callback_id = args.get("callbackId").get<double>();
2065 this->worker.add_job([this, callback_id, logic] {
2066 picojson::value response = picojson::value(picojson::object());
2067 picojson::object& async_out = response.get<picojson::object>();
2068 async_out["callbackId"] = picojson::value(callback_id);
2070 this->PostMessage(response.serialize().c_str());
2078 void FilesystemInstance::FileHandleReadData(const picojson::value& args, picojson::object& out) {
2080 CHECK_EXIST(args, "id", out)
2081 const int fh_id = static_cast<int>(args.get("id").get<double>());
2082 auto fh = opened_files.find(fh_id);
2084 if (opened_files.end() == fh) {
2085 LoggerE("FileHandle with id: %d not found", fh_id);
2086 LogAndReportError(IOException("Invalid FileHandle"), out);
2091 // We need to check how many bytes is it possible to read until the EOF.
2093 // We need to read from file exactly the minimum value of 'size' given by user and the
2094 // 'size ' to avoid returning array with redundant data (which would be equal to 0).
2095 size = file_bytes_to_eof(fh->second->file_handle);
2096 if (args.contains("size")) {
2097 // If user passed 'size' parameter, we need to read at most 'size' bytes.
2098 double size_double = args.get("size").get<double>();
2099 if (std::string::npos <= static_cast<unsigned long long>(size_double)) {
2100 LogAndReportError(InvalidValuesException("Invalid size was given"), out);
2103 size = std::min(static_cast<size_t>(size_double), size);
2105 } catch (const std::system_error& e) {
2106 LogAndReportError(IOException(e.what()), out);
2110 LoggerD("size: %zu", size);
2112 auto handle = fh->second;
2114 auto logic = [handle, size](decltype(out) out) {
2116 std::vector<std::uint8_t> data = read_file(handle->file_handle, size);
2117 out["result"] = picojson::value(picojson::string_type, true);
2118 encode_binary_in_string(data, out["result"].get<std::string>());
2119 } catch (std::runtime_error& e) {
2120 LoggerE("Cannot read, cause: %s", e.what());
2121 LogAndReportError(IOException(e.what()), out);
2125 bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2131 double callback_id = args.get("callbackId").get<double>();
2132 this->worker.add_job([this, callback_id, logic] {
2133 picojson::value response = picojson::value(picojson::object());
2134 picojson::object& async_out = response.get<picojson::object>();
2135 async_out["callbackId"] = picojson::value(callback_id);
2137 this->PostMessage(response.serialize().c_str());
2145 void FilesystemInstance::FileHandleWriteData(const picojson::value& args, picojson::object& out) {
2147 CHECK_EXIST(args, "id", out)
2148 CHECK_EXIST(args, "data", out)
2149 const auto& str = args.get("data").get<std::string>();
2150 const int fh_id = static_cast<int>(args.get("id").get<double>());
2152 auto fh = opened_files.find(fh_id);
2153 if (opened_files.end() == fh) {
2154 LoggerE("FileHandle with id: %d not found", fh_id);
2155 LogAndReportError(IOException("Invalid FileHandle"), out);
2159 auto handle = fh->second;
2161 auto logic = [str, handle](decltype(out) out) {
2163 std::vector<std::uint8_t> bytes;
2164 decode_binary_from_string(str, bytes);
2165 write_file(bytes.data(), bytes.size(), handle->file_handle);
2166 } catch (std::runtime_error& e) {
2167 LogAndReportError(IOException(e.what()), out);
2171 bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2177 double callback_id = args.get("callbackId").get<double>();
2178 this->worker.add_job([this, callback_id, logic] {
2179 picojson::value response = picojson::value(picojson::object());
2180 picojson::object& async_out = response.get<picojson::object>();
2181 async_out["callbackId"] = picojson::value(callback_id);
2183 this->PostMessage(response.serialize().c_str());
2191 void FilesystemInstance::FileHandleFlush(const picojson::value& args, picojson::object& out) {
2193 const int fh_id = static_cast<int>(args.get("id").get<double>());
2195 auto fh = opened_files.find(fh_id);
2196 if (opened_files.end() == fh) {
2197 LogAndReportError(IOException("Invalid FileHandle"), out);
2201 auto handle = fh->second;
2203 auto logic = [handle](decltype(out) out) {
2204 int ret = fflush(handle->file_handle);
2206 std::string error_message =
2207 std::string("flush failed, error message: ") + GetErrorString(errno);
2208 LogAndReportError(IOException(error_message.c_str()), out);
2212 bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2218 double callback_id = args.get("callbackId").get<double>();
2219 this->worker.add_job([this, callback_id, logic] {
2220 picojson::value response = picojson::value(picojson::object());
2221 picojson::object& async_out = response.get<picojson::object>();
2222 async_out["callbackId"] = picojson::value(callback_id);
2224 this->PostMessage(response.serialize().c_str());
2232 void FilesystemInstance::FileHandleSync(const picojson::value& args, picojson::object& out) {
2234 const int fh_id = static_cast<int>(args.get("id").get<double>());
2236 auto fh = opened_files.find(fh_id);
2237 if (opened_files.end() == fh) {
2238 LogAndReportError(IOException("Invalid FileHandle"), out);
2242 auto handle = fh->second;
2244 auto logic = [handle](decltype(out) out) {
2245 int ret = fsync(fileno(handle->file_handle));
2247 std::string error_message =
2248 std::string("sync failed, error message: ") + GetErrorString(errno);
2249 LogAndReportError(IOException(error_message.c_str()), out);
2253 bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2259 double callback_id = args.get("callbackId").get<double>();
2260 this->worker.add_job([this, callback_id, logic] {
2261 picojson::value response = picojson::value(picojson::object());
2262 picojson::object& async_out = response.get<picojson::object>();
2263 async_out["callbackId"] = picojson::value(callback_id);
2265 this->PostMessage(response.serialize().c_str());
2273 void FilesystemInstance::FileHandleClose(const picojson::value& args, picojson::object& out) {
2275 const int fh_id = static_cast<int>(args.get("id").get<double>());
2276 auto fh = opened_files.find(fh_id);
2277 if (opened_files.end() == fh) {
2278 LogAndReportError(IOException("Invalid FileHandle"), out);
2282 std::shared_ptr<FileHandle> handle = fh->second;
2283 opened_files.erase(fh);
2285 auto logic = [handle](decltype(out) out) {
2286 if (!handle->file_handle) {
2287 LogAndReportError(IOException("File handle already closed."), out);
2290 int ret = fclose(handle->file_handle);
2291 handle->file_handle = nullptr;
2293 std::string error_message =
2294 std::string("close failed, error message: ") + GetErrorString(errno);
2295 LogAndReportError(IOException(error_message.c_str()), out);
2299 bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2305 std::condition_variable conditional_variable;
2306 // adding empty job to worker's queue, in order to wait for all jobs to be done before closing
2308 this->worker.add_job([] {},
2309 [&conditional_variable, &mutex, &ready, &done, logic, &out] {
2311 std::unique_lock<std::mutex> lock(mutex);
2312 conditional_variable.wait(lock, [&ready] { return ready; });
2316 conditional_variable.notify_one();
2320 // let know that close is ready
2321 std::unique_lock<std::mutex> lock(mutex);
2324 conditional_variable.notify_one();
2328 std::unique_lock<std::mutex> lock(mutex);
2329 conditional_variable.wait(lock, [&done] { return done; });
2331 handle->file_handle = nullptr;
2334 double callback_id = args.get("callbackId").get<double>();
2335 this->worker.add_job([this, callback_id, logic] {
2336 picojson::value response = picojson::value(picojson::object());
2337 picojson::object& async_out = response.get<picojson::object>();
2338 async_out["callbackId"] = picojson::value(callback_id);
2340 this->PostMessage(response.serialize().c_str());
2350 } // namespace filesystem
2351 } // namespace extension