[Filesystem] File read and write native API
authorKamil Lysik <k.lysik@samsung.com>
Tue, 17 Feb 2015 12:44:05 +0000 (13:44 +0100)
committerKamil Lysik <k.lysik@samsung.com>
Tue, 3 Mar 2015 11:14:21 +0000 (12:14 +0100)
Change-Id: If8aadda4ae5cf167d3e2c974a78c0da4df267466
Signed-off-by: Kamil Lysik <k.lysik@samsung.com>
src/filesystem/filesystem.gyp
src/filesystem/filesystem_file.cc [new file with mode: 0644]
src/filesystem/filesystem_file.h [new file with mode: 0644]
src/filesystem/filesystem_instance.cc
src/filesystem/filesystem_instance.h
src/filesystem/filesystem_manager.cc
src/filesystem/filesystem_manager.h

index a8fcba9..9c49be7 100644 (file)
@@ -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 (file)
index 0000000..eb810e5
--- /dev/null
@@ -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 <stdio.h>
+#include <unistd.h>
+#include <common/scope_exit.h>
+#include <common/logger.h>
+
+namespace extension {
+namespace filesystem {
+
+namespace {
+uint8_t characterToNumber(char c) {
+  if (c == '+') {
+    return 62;
+  }
+  if (c == '/') {
+    return 63;
+  }
+  if (c <= '9') {
+    return c + 0x04;
+  }
+  if (c <= 'Z') {
+    return c - 0x41;
+  }
+  if (c <= 'z') {
+    return c - 0x47;
+  }
+  return 0;
+}
+
+char numberToCharacter(uint8_t i) {
+  if (i <= 25) {
+    return 'A' + i;
+  }
+  if (i <= 51) {
+    return 'a' + i - 26;
+  }
+  if (i <= 61) {
+    return '0' + i - 52;
+  }
+  if (i == 62) {
+    return '+';
+  }
+  if (i == 63) {
+    return '/';
+  }
+  return 0;
+}
+
+bool validateCharacter(char c) {
+  if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
+      (c >= 'A' && c <= 'Z') || (c == '=') || (c == '+') || (c == '/')) {
+    return true;
+  }
+  return false;
+}
+}  // namespace
+
+/**
+ * Data is encoded using Base64 encoding.
+ */
+
+bool FilesystemBuffer::DecodeData(const std::string& data) {
+  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<uint8_t*>(data.data());
+  size_t data_size = data.size();
+  while (written < data.size()) {
+    size_t part = fwrite(data_p, 1, data_size, file);
+
+    if (ferror(file)) {
+      LoggerE("Error during file write!");
+      return false;
+    }
+
+    written += part;
+    data_p += part;
+    data_size -= part;
+  }
+
+  status = fflush(file);
+  if (status) {
+    LoggerE("Cannot flush file!");
+    return false;
+  }
+
+  status = fsync(fileno(file));
+  if (status) {
+    LoggerE("Cannot sync file!");
+    return false;
+  }
+  LoggerD("Written %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 (file)
index 0000000..8746cff
--- /dev/null
@@ -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 <string>
+#include <vector>
+#include <stdint.h>
+
+namespace extension {
+namespace filesystem {
+
+class FilesystemBuffer : public std::vector<uint8_t> {
+ 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
index 8d3dd31..6757b56 100644 (file)
@@ -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<double>();
+  const std::string& location = args.get("location").get<std::string>();
+  size_t offset = static_cast<size_t>(args.get("offset").get<double>());
+  size_t length = static_cast<size_t>(args.get("length").get<double>());
+
+  auto onSuccess = [this, callback_id](const std::string& data) {
+    LoggerD("enter");
+    picojson::value response = picojson::value(picojson::object());
+    picojson::object& obj = response.get<picojson::object>();
+    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<picojson::object>();
+    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<std::string>();
+  size_t offset = static_cast<size_t>(args.get("offset").get<double>());
+  size_t length = static_cast<size_t>(args.get("length").get<double>());
+
+  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<double>();
+  const std::string& location = args.get("location").get<std::string>();
+  const std::string& data = args.get("data").get<std::string>();
+  size_t offset = static_cast<size_t>(args.get("location").get<double>());
+
+  auto onSuccess = [this, callback_id]() {
+    LoggerD("enter");
+    picojson::value response = picojson::value(picojson::object());
+    picojson::object& obj = response.get<picojson::object>();
+    obj["callbackId"] = picojson::value(callback_id);
+    ReportSuccess(obj);
+    PostMessage(response.serialize().c_str());
+  };
+
+  auto onError = [this, callback_id](FilesystemError e) {
+    LoggerD("enter");
+    picojson::value response = picojson::value(picojson::object());
+    picojson::object& obj = response.get<picojson::object>();
+    obj["callbackId"] = picojson::value(callback_id);
+    PrepareError(e, obj);
+    PostMessage(response.serialize().c_str());
+  };
+
+  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<std::string>();
+  const std::string& data = args.get("data").get<std::string>();
+  size_t offset = static_cast<size_t>(args.get("offset").get<double>());
+
+  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");
index 9f69e8d..7bb408a 100644 (file)
@@ -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,
index 7af0624..4a725b9 100644 (file)
@@ -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<void(const std::vector<std::string>&)>& success_cb,
-        const std::function<void(FilesystemError)>& error_cb) {
+    const std::string& path,
+    const std::function<void(const std::vector<std::string>&)>& success_cb,
+    const std::function<void(FilesystemError)>& error_cb) {
   LoggerD("entered");
 
   std::vector<std::string> fileList;
-  DIR *dp = nullptr;
+  DIRdp = nullptr;
   struct dirent entry;
-  struct dirent *result = nullptr;
+  struct direntresult = 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<void()>& success_cb,
-        const std::function<void(FilesystemError)>& error_cb) {
+    const std::string& path,
+    const std::function<void()>& success_cb,
+    const std::function<void(FilesystemError)>& 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<void()>& success_cb,
-        const std::function<void(FilesystemError)>& error_cb) {
+    const std::string& path,
+    const std::function<void()>& success_cb,
+    const std::function<void(FilesystemError)>& 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<void(const std::string&)>& success_cb,
+    const std::function<void(FilesystemError)>& 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<void()>& success_cb,
+    const std::function<void(FilesystemError)>& 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");
 
index d1d44c1..d25a814 100644 (file)
@@ -63,7 +63,7 @@ class FilesystemManager {
 
   void MakeDirectory(const std::string& path,
                      const std::function<void(FilesystemError)>& result_cb);
-  
+
   void ReadDir(
           const std::string& path,
           const std::function<void(const std::vector<std::string>&)>& success_cb,
@@ -73,6 +73,19 @@ class FilesystemManager {
           const std::string& path,
           const std::function<void()>& success_cb,
           const std::function<void(FilesystemError)>& error_cb);
+
+  void FileRead(const std::string& path,
+                size_t offset,
+                size_t length,
+                const std::function<void(const std::string&)>& success_cb,
+                const std::function<void(FilesystemError)>& error_cb);
+
+  void FileWrite(const std::string& path,
+                 const std::string& data,
+                 size_t offset,
+                 const std::function<void()>& success_cb,
+                 const std::function<void(FilesystemError)>& error_cb);
+
   void StartListening();
   void StopListening();
   void AddListener(FilesystemStateChangeListener* listener);