[Download] Fix and complete Download API
authorRomuald Texier-Marcadé <romuald.texier-marcade@open.eurogiciel.org>
Wed, 9 Jul 2014 16:24:40 +0000 (18:24 +0200)
committerCorentin Lecouvey <corentin.lecouvey@open.eurogiciel.org>
Mon, 18 Aug 2014 09:00:13 +0000 (11:00 +0200)
Refactored Filesytem and Download API to share code
related to virtual roots.
Pass TCT tests.
Fix example.

BUG=XWALK-1070

common/virtual_fs.cc [new file with mode: 0644]
common/virtual_fs.h [new file with mode: 0644]
download/download.gyp
download/download_api.js
download/download_instance.cc
download/download_instance.h
download/download_instance_tizen.cc
examples/download.html
filesystem/filesystem.gyp
filesystem/filesystem_instance.cc
filesystem/filesystem_instance.h

diff --git a/common/virtual_fs.cc b/common/virtual_fs.cc
new file mode 100644 (file)
index 0000000..93984a0
--- /dev/null
@@ -0,0 +1,317 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "common/virtual_fs.h"
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <pkgmgr-info.h>
+#include <tzplatform_config.h>
+
+#include <cassert>
+#include <algorithm>
+#include <sstream>
+#include <string>
+
+#include "common/extension.h"
+
+namespace {
+
+const char kInternalStorage[] = "internal";
+const char kRemovableStorage[] = "removable";
+
+const char kStorageTypeInternal[] = "INTERNAL";
+const char kStorageTypeExternal[] = "EXTERNAL";
+const char kStorageStateMounted[] = "MOUNTED";
+const char kStorageStateRemoved[] = "REMOVED";
+const char kStorageStateUnmountable[] = "UNMOUNTABLE";
+
+}  // namespace
+
+namespace vfs_const {
+
+const unsigned kDefaultFileMode = 0755;
+const char kLocationCamera[] = "camera";
+const char kLocationMusic[] = "music";
+const char kLocationImages[] = "images";
+const char kLocationVideos[] = "videos";
+const char kLocationDownloads[] = "downloads";
+const char kLocationDocuments[] = "documents";
+const char kLocationRingtones[] = "ringtones";
+const char kLocationWgtPackage[] = "wgt-package";
+const char kLocationWgtPrivate[] = "wgt-private";
+const char kLocationWgtPrivateTmp[] = "wgt-private-tmp";
+
+}  // namespace vfs_const
+
+VirtualFS::VirtualFS() {
+  std::string app_path = GetApplicationPath();
+  if (!app_path.empty()) {
+    AddInternalStorage(vfs_const::kLocationWgtPackage, app_path);
+    AddInternalStorage(vfs_const::kLocationWgtPrivate, JoinPath(app_path, "private"));
+    AddInternalStorage(vfs_const::kLocationWgtPrivateTmp, JoinPath(app_path, "tmp"));
+  }
+
+  AddInternalStorage(vfs_const::kLocationCamera, tzplatform_getenv(TZ_USER_CAMERA));
+  AddInternalStorage(vfs_const::kLocationMusic, tzplatform_getenv(TZ_USER_SOUNDS));
+  AddInternalStorage(vfs_const::kLocationImages, tzplatform_getenv(TZ_USER_IMAGES));
+  AddInternalStorage(vfs_const::kLocationVideos, tzplatform_getenv(TZ_USER_VIDEOS));
+  AddInternalStorage(vfs_const::kLocationDownloads, tzplatform_getenv(TZ_USER_DOWNLOADS));
+  AddInternalStorage(vfs_const::kLocationDocuments, tzplatform_getenv(TZ_USER_DOCUMENTS));
+  AddInternalStorage(vfs_const::kLocationRingtones,
+      tzplatform_mkpath(TZ_USER_SHARE, "settings/Ringtones"));
+  storage_changed_cb_ = NULL;
+  cb_user_data_ = NULL;
+}
+
+VirtualFS::~VirtualFS() {
+}
+
+std::string VirtualFS::JoinPath(const std::string& one,
+    const std::string& another) {
+  return one + '/' + another;
+}
+
+bool VirtualFS::MakePath(const std::string& path, int mode) {
+  // Path should start with '/' and contain at least 1 character after '/'.
+  if (path.empty() || path[0] != '/' || path.length() < 2)
+    return false;
+
+  struct stat st;
+  std::string dir = path;
+  if (stat(dir.c_str(), &st) == 0)
+    return true;
+
+  // Add trailing '/' if missing, so we can iterate till the end of the path.
+  if (dir[dir.size() - 1] != '/')
+    dir += '/';
+
+  for (std::string::iterator iter = dir.begin(); iter != dir.end();) {
+    std::string::iterator cur_iter = std::find(iter, dir.end(), '/');
+
+    // If '/' is found at the beginning of the string, iterate to the next '/'.
+    if (cur_iter == iter) {
+      ++iter;
+      cur_iter = std::find(iter, dir.end(), '/');
+    }
+
+    std::string new_path = std::string(dir.begin(), cur_iter);
+
+    // If path doesn't exist, try to create one and continue iteration.
+    // In case of error, stop iteration and return.
+    if (stat(new_path.c_str(), &st) != 0) {
+      if (mkdir(new_path.c_str(), mode) != 0 && errno != EEXIST )
+          return false;
+    // If path exists and it is not a directory, stop iteration and return.
+    } else if (!S_ISDIR(st.st_mode)) {
+      return false;
+    }
+
+    // Advance iterator and create next parent folder.
+    iter = cur_iter;
+    if (cur_iter != dir.end())
+      ++iter;
+  }
+  return true;
+}
+
+int VirtualFS::GetDirEntryCount(const char* path) {
+  int count = 0;
+  DIR* dir = opendir(path);
+  if (!dir)
+    return count;
+
+  struct dirent entry;
+  struct dirent *result;
+  int ret = readdir_r(dir, &entry, &result);
+
+  for (; ret == 0 && result != NULL; ret = readdir_r(dir, &entry, &result)) {
+    if (entry.d_type == DT_REG || entry.d_type == DT_DIR)
+      count++;
+  }
+
+  closedir(dir);
+  return count;
+}
+
+std::string VirtualFS::GetAppId(const std::string& package_id) {
+  char* appid = NULL;
+  pkgmgrinfo_pkginfo_h pkginfo_handle;
+  int ret = pkgmgrinfo_pkginfo_get_pkginfo(package_id.c_str(), &pkginfo_handle);
+  if (ret != PMINFO_R_OK)
+    return std::string();
+  ret = pkgmgrinfo_pkginfo_get_mainappid(pkginfo_handle, &appid);
+  if (ret != PMINFO_R_OK) {
+    pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle);
+    return std::string();
+  }
+
+  std::string retval(appid);
+  pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle);
+  return retval;
+}
+
+std::string VirtualFS::GetExecPath(const std::string& app_id) {
+  char* exec_path = NULL;
+  pkgmgrinfo_appinfo_h appinfo_handle;
+  int ret = pkgmgrinfo_appinfo_get_appinfo(app_id.c_str(), &appinfo_handle);
+  if (ret != PMINFO_R_OK)
+    return std::string();
+  ret = pkgmgrinfo_appinfo_get_exec(appinfo_handle, &exec_path);
+  if (ret != PMINFO_R_OK) {
+    pkgmgrinfo_appinfo_destroy_appinfo(appinfo_handle);
+    return std::string();
+  }
+
+  std::string retval(exec_path);
+  pkgmgrinfo_appinfo_destroy_appinfo(appinfo_handle);
+  return retval;
+}
+
+bool VirtualFS::GetStorageByLabel(const std::string& label, Storage& storage) {
+  storage_foreach_device_supported(OnStorageDeviceSupported, this);
+  Storages::const_iterator it = storages_.find(label);
+
+  if (it == storages_.end()) {
+    return false;
+  }
+  storage = it->second;
+  return true;
+}
+
+Storages::iterator VirtualFS::begin() {
+  storage_foreach_device_supported(OnStorageDeviceSupported, this);
+  return storages_.begin();
+}
+
+Storages::const_iterator VirtualFS::end() const {
+  return storages_.end();
+}
+
+std::string VirtualFS::GetApplicationPath() {
+  std::string id_str = common::Extension::GetRuntimeVariable("app_id", 64);
+  std::string pkg_id = id_str.substr(1, id_str.rfind('"') - 1);
+  if (pkg_id.empty())
+    return std::string();
+  std::string app_id = GetAppId(pkg_id);
+  if (app_id.empty())
+    return std::string();
+  std::string exec_path = GetExecPath(app_id);
+  if (exec_path.empty())
+    return std::string();
+
+  size_t index = exec_path.find(pkg_id);
+  if (index != std::string::npos)
+    return exec_path.substr(0, index + pkg_id.length());
+  return std::string();
+}
+
+std::string VirtualFS::GetRealPath(const std::string& fullPath) const {
+  std::size_t pos = fullPath.find_first_of('/');
+  Storages::const_iterator it = storages_.find(fullPath.substr(0, pos));
+
+  if (it == storages_.end())
+    return std::string();
+
+  if (pos != std::string::npos)
+    return it->second.GetFullPath() + fullPath.substr(pos);
+
+  return it->second.GetFullPath();
+}
+
+void VirtualFS::AddInternalStorage(
+    const std::string& label, const std::string& path) {
+  if (MakePath(path, vfs_const::kDefaultFileMode))
+    storages_.insert(StorageLabelPair(label,
+                                      Storage(-1,
+                                      Storage::STORAGE_TYPE_INTERNAL,
+                                      Storage::STORAGE_STATE_MOUNTED,
+                                      path)));
+}
+
+void VirtualFS::AddStorage(int id,
+                           storage_type_e type,
+                           storage_state_e state,
+                           const std::string& path) {
+  std::string label;
+  if (type == STORAGE_TYPE_INTERNAL)
+    label = kInternalStorage + std::to_string(id);
+  else if (type == STORAGE_TYPE_EXTERNAL)
+    label = kRemovableStorage + std::to_string(id);
+
+  storages_.insert(StorageLabelPair(label,
+                                    Storage(id,
+                                    type,
+                                    state,
+                                    path)));
+  if (std::find(watched_storages_.begin(),
+                watched_storages_.end(), id) != watched_storages_.end()) {
+    watched_storages_.push_back(id);
+    storage_set_state_changed_cb(id, OnStorageStateChanged, this);
+  }
+}
+
+void VirtualFS::SetOnStorageChangedCb(CallBackFunctionPtr cb, void* user_data) {
+  storage_changed_cb_ = cb;
+  cb_user_data_ = user_data;
+}
+
+void VirtualFS::NotifyStorageStateChanged(int id, storage_state_e state) {
+  for (Storages::iterator it = storages_.begin(); it != storages_.end(); ++it) {
+    if (it->second.GetId() == id) {
+      it->second.SetState(state);
+      if (storage_changed_cb_) {
+        storage_changed_cb_(it->first, it->second, cb_user_data_);
+      }
+      break;
+    }
+  }
+}
+
+bool VirtualFS::OnStorageDeviceSupported(
+    int id, storage_type_e type, storage_state_e state,
+    const char* path, void* user_data) {
+  reinterpret_cast<VirtualFS*>(user_data)->AddStorage(
+      id, type, state, path);
+  return true;
+}
+
+void VirtualFS::OnStorageStateChanged(
+    int id, storage_state_e state, void* user_data) {
+  reinterpret_cast<VirtualFS*>(user_data)->NotifyStorageStateChanged(
+      id, state);
+}
+
+/*
+ * Storage Class
+ */
+
+Storage::Storage(
+    int id, int type, int state, const std::string& fullpath)
+    : id_(id),
+      type_(type),
+      state_(state),
+      full_path_(fullpath) { }
+
+std::string Storage::GetType() const {
+  return (type_ == Storage::STORAGE_TYPE_INTERNAL) ? kStorageTypeInternal :
+      kStorageTypeExternal;
+}
+
+std::string Storage::GetState() const {
+  switch (state_) {
+    case Storage::STORAGE_STATE_MOUNTED:
+    case Storage::STORAGE_STATE_MOUNTED_READONLY:
+      return kStorageStateMounted;
+    case Storage::STORAGE_STATE_REMOVED:
+      return kStorageStateRemoved;
+    case Storage::STORAGE_STATE_UNMOUNTABLE:
+      return kStorageStateUnmountable;
+    default:
+      assert(!"Not reached");
+  }
+  return std::string();
+}
diff --git a/common/virtual_fs.h b/common/virtual_fs.h
new file mode 100644 (file)
index 0000000..94a988f
--- /dev/null
@@ -0,0 +1,130 @@
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMMON_VIRTUAL_FS_H_
+#define COMMON_VIRTUAL_FS_H_
+
+#include <appfw/app_storage.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace vfs_const {
+
+extern const unsigned kDefaultFileMode;
+extern const char kLocationCamera[];
+extern const char kLocationMusic[];
+extern const char kLocationImages[];
+extern const char kLocationVideos[];
+extern const char kLocationDownloads[];
+extern const char kLocationDocuments[];
+extern const char kLocationRingtones[];
+extern const char kLocationWgtPackage[];
+extern const char kLocationWgtPrivate[];
+extern const char kLocationWgtPrivateTmp[];
+
+}  // namespace vfs_const
+
+class Storage {
+ public:
+  /* Mapped to storage_type_e */
+  enum StorageType {
+    STORAGE_TYPE_INTERNAL, STORAGE_TYPE_EXTERNAL,
+  };
+
+  /* Mapped to storage_state_e */
+  enum StorageState {
+    STORAGE_STATE_UNMOUNTABLE = -2,
+    STORAGE_STATE_REMOVED = -1,
+    STORAGE_STATE_MOUNTED = 0,
+    STORAGE_STATE_MOUNTED_READONLY = 1,
+  };
+
+  Storage(int id = 0, int type = 0, int state = 0,
+      const std::string& fullpath = "");
+
+  std::string GetType() const;
+  std::string GetState() const;
+  int GetId() const { return id_; }
+  const std::string& GetFullPath() const { return full_path_; }
+  void SetState(int state) { state_ = state; }
+
+ private:
+  int id_;
+  int type_;
+  int state_;
+  std::string full_path_;
+};
+
+typedef std::map<std::string, Storage> Storages;
+typedef void(*CallBackFunctionPtr)(const std::string&, Storage, void*);
+
+/**
+ * The VirtualFS class provide an abstraction of the TIZEN virtual filesystem.
+ * It manages mounted storages and virtual roots, creating missing directories
+ * if needed.
+ * Convenient functions are also provided for working with paths (real or
+ * virtual).
+ */
+class VirtualFS {
+ public:
+  VirtualFS();
+  ~VirtualFS();
+  /**
+   * Resolve the given fullpath within the virtual filesystem to an absolute
+   * path within the real filesystem.
+   * @param fullPath: fully-qualified path of the form: <root name>/<path>
+   *    where <rootname> is the name of the virtual root and <path> is the path
+   *    to the file or directory relative to that root.
+   * @return full Linux path.
+   */
+  std::string GetRealPath(const std::string& fullPath) const;
+  bool GetStorageByLabel(const std::string& label, Storage& storage);
+  Storages::iterator begin();
+  Storages::const_iterator end() const;
+  void SetOnStorageChangedCb(CallBackFunctionPtr cb, void* user_data);
+
+  /**
+   * Concatenate two paths.
+   * @param one: base path.
+   * @param another: path within 'one'.
+   * @return new path.
+   */
+  static std::string JoinPath(const std::string& one,
+      const std::string& another);
+  /**
+   * Create full path and parent directories when needed.
+   * Similar to "mkdir -p".
+   * @param path: the path to be created.
+   * @param mode: the unix access mode applied to the new directories.
+   * @return true if success.
+   */
+  static bool MakePath(const std::string& path, int mode);
+  static int GetDirEntryCount(const char* path);
+  static std::string GetAppId(const std::string& package_id);
+  static std::string GetExecPath(const std::string& app_id);
+  static std::string GetApplicationPath();
+
+ private:
+  void AddInternalStorage(const std::string& label, const std::string& path);
+  void AddStorage(int storage, storage_type_e type, storage_state_e state,
+      const std::string& path);
+  void NotifyStorageStateChanged(int id, storage_state_e state);
+  static bool OnStorageDeviceSupported(int id, storage_type_e type,
+      storage_state_e state, const char* path, void* user_data);
+  static void OnStorageStateChanged(int id, storage_state_e state,
+      void* user_data);
+
+  CallBackFunctionPtr storage_changed_cb_;
+  void* cb_user_data_;
+
+  typedef std::pair<std::string, Storage> StorageLabelPair;
+  Storages storages_;
+  std::vector<int> watched_storages_;
+};
+
+#endif  // COMMON_VIRTUAL_FS_H_
index 9ef1cfb..65904ee 100644 (file)
@@ -15,6 +15,8 @@
         'download_instance_desktop.cc',
         'download_instance_tizen.cc',
         'download_utils.h',
+        '../common/virtual_fs.cc',
+        '../common/virtual_fs.h',
       ],
       'conditions': [
         ['tizen == 1', {
index 817fc2a..cbaa1ce 100644 (file)
@@ -19,7 +19,7 @@ var asValidString = function(o) {
 };
 
 var ensureType = function(o, expected) {
-  if (typeof o != expected) {
+  if (typeof o != expected || o === undefined) {
     throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
   }
 };
@@ -240,9 +240,7 @@ exports.start = function(request, listener) {
     throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
   }
   requests[request.uid] = request;
-  // If listener is equal to 'undefined', 'listener !== null' is still true.
-  // So change condition 'listener !== null' to 'listener != null'.
-  if (listener != null) {
+  if (arguments.length > 1 && listener !== null) {
     ensureType(listener, 'object');
     exports.setListener(request.uid, listener);
   }
index 62a8439..83bd257 100644 (file)
@@ -212,6 +212,12 @@ void DownloadInstance::HandleStart(const picojson::value& msg) {
   DownloadArgs* args = new DownloadArgs(d->uid, this);
   args_.push_back(args);
 
+  if (d->destination.empty()) {
+    OnFailedInfo(args,
+        ToString(EnumToPChar(DOWNLOAD_ERROR_INVALID_DESTINATION)));
+    return;
+  }
+
   // create and start
   CHECK(download_create(&d->download_id), args);
   CHECK(download_set_state_changed_cb(d->download_id, OnStateChanged,
index 558dd6b..c6d9a50 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "common/extension.h"
 #include "common/utils.h"
+#include "common/virtual_fs.h"
 #include "web/download.h"
 
 namespace picojson {
@@ -27,6 +28,7 @@ class DownloadInstance : public common::Instance {
   ~DownloadInstance();
 
  private:
+  VirtualFS vfs_;
   virtual void HandleMessage(const char* msg);
   virtual void HandleSyncMessage(const char* msg);
 
@@ -87,9 +89,7 @@ class DownloadInstance : public common::Instance {
   typedef std::map<std::string, DownloadItemRefPtr> DownloadItemMap;
   DownloadItemMap downloads_;
 
-  // TODO(hdq): This depends on filesystem api?
   const std::string GetFullDestinationPath(const std::string destination) const;
-  const std::string GetActualFolder(const std::string& destination) const;
 
   bool GetDownloadID(const picojson::value& msg,
                      int& download_id, DownloadArgs** args);
index cde9479..107d03f 100644 (file)
@@ -9,41 +9,12 @@
 #include <tzplatform_config.h>
 #include <unistd.h>
 
-const std::string DownloadInstance::GetActualFolder(
-    const std::string& destination) const {
-  typedef std::map<std::string, std::string> LocationMap;
-  static const LocationMap::value_type data[] = {
-     LocationMap::value_type("documents", "Documents"),
-     LocationMap::value_type("downloads", "Downloads"),
-     LocationMap::value_type("images", "Images"),
-     LocationMap::value_type("music", "Sounds"),
-     LocationMap::value_type("videos", "Videos"),
-  };
-  static const LocationMap locations(data, data + sizeof data / sizeof data[0]);
-  LocationMap::const_iterator location = locations.find(destination);
-  if (location == locations.end()) {
-    return destination;
-  } else {
-    return location->second;
-  }
-}
-
 const std::string DownloadInstance::GetFullDestinationPath(
     const std::string destination) const {
-  // TODO(hdq): User should be able to choose store to external storage
-  //            i.e. /opt/storage/sdcard/Downloads
-  const std::string default_folder("Downloads");
-  const std::string location =
-      destination.empty() ? default_folder : destination;
-  std::string path =
-      tzplatform_getenv(TZ_USER_CONTENT) + GetActualFolder(location);
-
-  // Create path if not exist
-  struct stat path_stat;
-  if (stat(path.c_str(), &path_stat) == -1
-      && mkdir(path.c_str(), 0777) != 0) {
-    path = tzplatform_getenv(TZ_USER_CONTENT) + default_folder;
-  }
-
-  return path;
+  std::string real_path;
+  if (destination.empty())
+    real_path = vfs_.GetRealPath(vfs_const::kLocationDownloads);
+  else
+    real_path = vfs_.GetRealPath(destination);
+  return real_path;
 }
index 11b09a0..490ca7f 100644 (file)
@@ -106,11 +106,11 @@ handle("button1", "Pause Download1", function() {
 });
 
 handle("bnstart2", "Start Download2", function() {
-  r2 = new tizen.DownloadRequest(url2, "Images"); // Test saving to specific folder
+  r2 = new tizen.DownloadRequest(url2, "images"); // Test saving to specific folder
 
   id2 = tizen.download.start(r2, listener);
   urls[id2] = url2;
-  output.value += "> asked to start download "+url2+" to Images folder\n";
+  output.value += "> asked to start download "+url2+" to images folder\n";
 });
 
 handle("button2", "Pause Download2", function() {
index e33f8fe..fb4b2b6 100644 (file)
@@ -19,6 +19,8 @@
         'filesystem_extension.h',
         'filesystem_instance.cc',
         'filesystem_instance.h',
+        '../common/virtual_fs.cc',
+        '../common/virtual_fs.h',
       ],
       'includes': [
         '../common/pkg-config.gypi',
index a357044..8885707 100644 (file)
@@ -8,7 +8,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <iconv.h>
-#include <pkgmgr-info.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <tzplatform_config.h>
 #include <utility>
 
 namespace {
-const unsigned kDefaultFileMode = 0755;
-
-const char kLocationCamera[] = "camera";
-const char kLocationMusic[] = "music";
-const char kLocationImages[] = "images";
-const char kLocationVideos[] = "videos";
-const char kLocationDownloads[] = "downloads";
-const char kLocationDocuments[] = "documents";
-const char kLocationRingtones[] = "ringtones";
-const char kLocationWgtPackage[] = "wgt-package";
-const char kLocationWgtPrivate[] = "wgt-private";
-const char kLocationWgtPrivateTmp[] = "wgt-private-tmp";
-
-const char kInternalStorage[] = "internal";
-const char kRemovableStorage[] = "removable";
-
-const char kStorageTypeInternal[] = "INTERNAL";
-const char kStorageTypeExternal[] = "EXTERNAL";
-const char kStorageStateMounted[] = "MOUNTED";
-const char kStorageStateRemoved[] = "REMOVED";
-const char kStorageStateUnmountable[] = "UNMOUNTABLE";
 
 const char kPlatformEncoding[] = "UTF-8";
 const size_t kBufferSize = 1024 * 4;
 
 unsigned int lastStreamId = 0;
 
-
 bool IsWritable(const struct stat& st) {
   if (st.st_mode & S_IWOTH)
     return true;
@@ -57,133 +34,22 @@ bool IsWritable(const struct stat& st) {
   return false;
 }
 
-std::string JoinPath(const std::string& one, const std::string& another) {
-  return one + '/' + another;
-}
-
-// This function creates full path and parent directories when needed.
-// Similar to "mkdir -p".
-bool makePath(const std::string& path) {
-  // Path should start with '/' and contain at least 1 character after '/'.
-  if (path.empty() || path[0] != '/' || path.length() < 2)
-    return false;
-
-  struct stat st;
-  std::string dir = path;
-  if (stat(dir.c_str(), &st) == 0)
-    return true;
-
-  // Add trailing '/' if missing, so we can iterate till the end of the path.
-  if (dir[dir.size() - 1] != '/')
-    dir += '/';
-
-  for (auto iter = dir.begin(); iter != dir.end();) {
-    auto cur_iter = std::find(iter, dir.end(), '/');
-
-    // If '/' is found at the beginning of the string, iterate to the next '/'.
-    if (cur_iter == iter) {
-      ++iter;
-      cur_iter = std::find(iter, dir.end(), '/');
-    }
-
-    std::string new_path = std::string(dir.begin(), cur_iter);
-
-    // If path doesn't exist, try to create one and continue iteration.
-    // In case of error, stop iteration and return.
-    if (stat(new_path.c_str(), &st) != 0) {
-      if (mkdir(new_path.c_str(), kDefaultFileMode) != 0 && errno != EEXIST )
-          return false;
-    // If path exists and it is not a directory, stop iteration and return.
-    } else if (!S_ISDIR(st.st_mode)) {
-      return false;
-    }
-
-    // Advance iterator and create next parent folder.
-    iter = cur_iter;
-    if (cur_iter != dir.end())
-      ++iter;
-  }
-  return true;
-}
-
-int get_dir_entry_count(const char* path) {
-  int count = 0;
-  DIR* dir = opendir(path);
-  if (!dir)
-    return count;
-
-  struct dirent entry;
-  struct dirent *result;
-  int ret = readdir_r(dir, &entry, &result);
-
-  for (; ret == 0 && result != NULL; ret = readdir_r(dir, &entry, &result)) {
-    if (entry.d_type == DT_REG || entry.d_type == DT_DIR)
-      count++;
-  }
-
-  closedir(dir);
-  return count;
-}
-
-std::string GetExecPath(const std::string& app_id) {
-  char* exec_path = NULL;
-  pkgmgrinfo_appinfo_h appinfo_handle;
-  int ret = pkgmgrinfo_appinfo_get_appinfo(app_id.c_str(), &appinfo_handle);
-  if (ret != PMINFO_R_OK)
-    return std::string();
-  ret = pkgmgrinfo_appinfo_get_exec(appinfo_handle, &exec_path);
-  if (ret != PMINFO_R_OK) {
-    pkgmgrinfo_appinfo_destroy_appinfo(appinfo_handle);
-    return std::string();
-  }
-
-  std::string retval(exec_path);
-  pkgmgrinfo_appinfo_destroy_appinfo(appinfo_handle);
-  return retval;
+picojson::object StorageToJSON(Storage storage,
+    const std::string& label) {
+  picojson::object storage_object;
+  storage_object["label"] = picojson::value(label);
+  storage_object["type"] = picojson::value(storage.GetType());
+  storage_object["state"] = picojson::value(storage.GetState());
+  return storage_object;
 }
 
-std::string GetApplicationPath() {
-  std::string id_str = common::Extension::GetRuntimeVariable("app_id", 64);
-  picojson::value id_val;
-  std::istringstream buf(id_str);
-  std::string error = picojson::parse(id_val, buf);
-  if (!error.empty()) {
-    std::cerr << "Got invalid package ID." << std::endl;
-    return std::string();
-  }
-
-  std::string app_id = id_val.get<std::string>();
-  if (app_id.empty())
-    return std::string();
-
-  std::string exec_path = GetExecPath(app_id);
-  if (exec_path.empty())
-    return std::string();
+}  // namespace
 
-  size_t index = exec_path.find(app_id);
-  if (index != std::string::npos)
-    return exec_path.substr(0, index + app_id.length());
-  return std::string();
+FilesystemInstance::FilesystemInstance() {
 }
 
-};  // namespace
-
-FilesystemInstance::FilesystemInstance() {
-  std::string app_path = GetApplicationPath();
-  if (!app_path.empty()) {
-    AddInternalStorage(kLocationWgtPackage, app_path);
-    AddInternalStorage(kLocationWgtPrivate, JoinPath(app_path, "private"));
-    AddInternalStorage(kLocationWgtPrivateTmp, JoinPath(app_path, "tmp"));
-  }
-
-  AddInternalStorage(kLocationCamera, tzplatform_getenv(TZ_USER_CAMERA));
-  AddInternalStorage(kLocationMusic, tzplatform_getenv(TZ_USER_SOUNDS));
-  AddInternalStorage(kLocationImages, tzplatform_getenv(TZ_USER_IMAGES));
-  AddInternalStorage(kLocationVideos, tzplatform_getenv(TZ_USER_VIDEOS));
-  AddInternalStorage(kLocationDownloads, tzplatform_getenv(TZ_USER_DOWNLOADS));
-  AddInternalStorage(kLocationDocuments, tzplatform_getenv(TZ_USER_DOCUMENTS));
-  AddInternalStorage(kLocationRingtones, \
-    tzplatform_mkpath(TZ_USER_SHARE, "settings/Ringtones"));
+void FilesystemInstance::Initialize() {
+  vfs_.SetOnStorageChangedCb(OnStorageStateChanged, this);
 }
 
 FilesystemInstance::~FilesystemInstance() {
@@ -273,8 +139,8 @@ void FilesystemInstance::HandleFileSystemManagerResolve(
 
   mode = msg.contains("mode") ? msg.get("mode").to_str() : "rw";
 
-  size_t pos_wgt_pkg = location.find(kLocationWgtPackage);
-  size_t pos_ringtones = location.find(kLocationRingtones);
+  size_t pos_wgt_pkg = location.find(vfs_const::kLocationWgtPackage);
+  size_t pos_ringtones = location.find(vfs_const::kLocationRingtones);
 
   if (pos_wgt_pkg != std::string::npos || pos_ringtones != std::string::npos) {
     if (mode == "w" || mode == "rw" || mode == "a") {
@@ -286,7 +152,7 @@ void FilesystemInstance::HandleFileSystemManagerResolve(
 
   if (pos_wgt_pkg != std::string::npos ||
       pos_ringtones != std::string::npos ||
-      location.find(kLocationWgtPrivate) != std::string::npos)
+      location.find(vfs_const::kLocationWgtPrivate) != std::string::npos)
     check_if_inside_default = false;
 
   std::string real_path;
@@ -294,7 +160,7 @@ void FilesystemInstance::HandleFileSystemManagerResolve(
     real_path = location.substr(sizeof("file://") - 1);
     check_if_inside_default = false;
   } else {
-    real_path = GetRealPath(location);
+    real_path = vfs_.GetRealPath(location);
   }
 
   if (real_path.empty()) {
@@ -340,25 +206,23 @@ void FilesystemInstance::HandleFileSystemManagerResolve(
 
 void FilesystemInstance::HandleFileSystemManagerGetStorage(
       const picojson::value& msg) {
-  storage_foreach_device_supported(OnStorageDeviceSupported, this);
-  Storages::const_iterator it = storages_.find(msg.get("label").to_str());
-
-  if (it == storages_.end()) {
+  Storage storage;
+  std::string label = msg.get("label").to_str();
+  if (!vfs_.GetStorageByLabel(label, storage)) {
     PostAsyncErrorReply(msg, NOT_FOUND_ERR);
     return;
   }
 
-  picojson::object storage_object = it->second.toJSON(it->first);
+  picojson::object storage_object = StorageToJSON(storage, label);
   PostAsyncSuccessReply(msg, storage_object);
 }
 
 void FilesystemInstance::HandleFileSystemManagerListStorages(
       const picojson::value& msg) {
-  storage_foreach_device_supported(OnStorageDeviceSupported, this);
   picojson::array storage_objects;
-  Storages::const_iterator it = storages_.begin();
-  while (it != storages_.end()) {
-    picojson::object storage_object = it->second.toJSON(it->first);
+  Storages::const_iterator it = vfs_.begin();
+  while (it != vfs_.end()) {
+    picojson::object storage_object = StorageToJSON(it->second, it->first);
     storage_objects.push_back(picojson::value(storage_object));
     ++it;
   }
@@ -407,7 +271,7 @@ void FilesystemInstance::HandleFileOpenStream(const picojson::value& msg) {
     return;
   }
 
-  std::string real_path = GetRealPath(msg.get("fullPath").to_str());
+  std::string real_path = vfs_.GetRealPath(msg.get("fullPath").to_str());
   char* real_path_cstr = realpath(real_path.c_str(), NULL);
   if (!real_path_cstr) {
     free(real_path_cstr);
@@ -487,7 +351,7 @@ void FilesystemInstance::HandleFileDeleteDirectory(const picojson::value& msg) {
   }
 
   bool recursive = msg.get("recursive").evaluate_as_boolean();
-  std::string real_path = GetRealPath(msg.get("directoryPath").to_str());
+  std::string real_path = vfs_.GetRealPath(msg.get("directoryPath").to_str());
   if (real_path.empty()) {
     PostAsyncErrorReply(msg, INVALID_VALUES_ERR);
     return;
@@ -512,7 +376,7 @@ void FilesystemInstance::HandleFileDeleteFile(const picojson::value& msg) {
     return;
   }
 
-  std::string real_path = GetRealPath(msg.get("filePath").to_str());
+  std::string real_path = vfs_.GetRealPath(msg.get("filePath").to_str());
   if (real_path.empty()) {
     PostAsyncErrorReply(msg, INVALID_VALUES_ERR);
     return;
@@ -546,7 +410,7 @@ void FilesystemInstance::HandleFileListFiles(const picojson::value& msg) {
     return;
   }
 
-  std::string real_path = GetRealPath(msg.get("fullPath").to_str());
+  std::string real_path = vfs_.GetRealPath(msg.get("fullPath").to_str());
   if (real_path.empty()) {
     PostAsyncErrorReply(msg, INVALID_VALUES_ERR);
     return;
@@ -567,8 +431,8 @@ void FilesystemInstance::HandleFileListFiles(const picojson::value& msg) {
     if (!strcmp(entry.d_name, ".") || !strcmp(entry.d_name, ".."))
       continue;
 
-    a.push_back(picojson::value(JoinPath(msg.get("fullPath").to_str(),
-                                         entry.d_name)));
+    a.push_back(picojson::value(VirtualFS::JoinPath(
+        msg.get("fullPath").to_str(), entry.d_name)));
   }
 
   closedir(directory);
@@ -668,7 +532,7 @@ class PosixFile {
   bool unlink_when_done_;
  public:
   PosixFile(const std::string& path, int mode)
-      : fd_(open(path.c_str(), mode, kDefaultFileMode))
+      : fd_(open(path.c_str(), mode, vfs_const::kDefaultFileMode))
       , mode_(mode)
       , path_(path)
       , unlink_when_done_(mode & O_CREAT) {}
@@ -758,7 +622,7 @@ bool CopyElement(const std::string &from, const std::string &to) {
   }  // end file case
 
   // element is a directory, create if not exists
-  int status = mkdir(to.c_str(), kDefaultFileMode);
+  int status = mkdir(to.c_str(), vfs_const::kDefaultFileMode);
   if (status != 0 && errno != EEXIST) {
     std::cerr << "failed to create destination dir: " << to << std::endl;
     return false;
@@ -796,10 +660,10 @@ void FilesystemInstance::HandleFileCopyTo(const picojson::value& msg) {
 
   bool overwrite = msg.get("overwrite").evaluate_as_boolean();
   std::string real_origin_path =
-      GetRealPath(msg.get("originFilePath").to_str());
+      vfs_.GetRealPath(msg.get("originFilePath").to_str());
   std::string real_destination_path =
       ResolveImplicitDestination(real_origin_path,
-      GetRealPath(msg.get("destinationFilePath").to_str()));
+      vfs_.GetRealPath(msg.get("destinationFilePath").to_str()));
 
   if (!CopyAndRenameSanityChecks(msg, real_origin_path, real_destination_path,
                                  overwrite))
@@ -822,10 +686,10 @@ void FilesystemInstance::HandleFileMoveTo(const picojson::value& msg) {
 
   bool overwrite = msg.get("overwrite").evaluate_as_boolean();
   std::string real_origin_path =
-      GetRealPath(msg.get("originFilePath").to_str());
+      vfs_.GetRealPath(msg.get("originFilePath").to_str());
   std::string real_destination_path =
       ResolveImplicitDestination(real_origin_path,
-      GetRealPath(msg.get("destinationFilePath").to_str()));
+      vfs_.GetRealPath(msg.get("destinationFilePath").to_str()));
 
   if (!CopyAndRenameSanityChecks(msg, real_origin_path, real_destination_path,
                                  overwrite))
@@ -1243,20 +1107,20 @@ void FilesystemInstance::HandleFileCreateDirectory(const picojson::value& msg,
     return;
   }
 
-  std::string full_path = JoinPath(msg.get("fullPath").to_str(),
+  std::string full_path = VirtualFS::JoinPath(msg.get("fullPath").to_str(),
                                    msg.get("relativeDirPath").to_str());
   if (full_path.empty()) {
     SetSyncError(reply, INVALID_VALUES_ERR);
     return;
   }
 
-  std::string real_path = GetRealPath(full_path);
+  std::string real_path = vfs_.GetRealPath(full_path);
   if (real_path.empty()) {
     SetSyncError(reply, INVALID_VALUES_ERR);
     return;
   }
 
-  if (!makePath(real_path)) {
+  if (!VirtualFS::MakePath(real_path, vfs_const::kDefaultFileMode)) {
     SetSyncError(reply, IO_ERR);
     return;
   }
@@ -1275,21 +1139,22 @@ void FilesystemInstance::HandleFileCreateFile(const picojson::value& msg,
     return;
   }
 
-  std::string full_path = JoinPath(msg.get("fullPath").to_str(),
-                                   msg.get("relativeFilePath").to_str());
+  std::string full_path = VirtualFS::JoinPath(
+      msg.get("fullPath").to_str(),
+      msg.get("relativeFilePath").to_str());
   if (full_path.empty()) {
     SetSyncError(reply, INVALID_VALUES_ERR);
     return;
   }
 
-  std::string real_path = GetRealPath(full_path);
+  std::string real_path = vfs_.GetRealPath(full_path);
   if (real_path.empty()) {
     SetSyncError(reply, INVALID_VALUES_ERR);
     return;
   }
 
   int result = open(real_path.c_str(), O_CREAT | O_WRONLY | O_EXCL,
-        kDefaultFileMode);
+      vfs_const::kDefaultFileMode);
   if (result < 0) {
     SetSyncError(reply, IO_ERR);
     return;
@@ -1307,7 +1172,7 @@ void FilesystemInstance::HandleFileGetURI(const picojson::value& msg,
   }
   std::string full_path = msg.get("fullPath").to_str();
 
-  std::string real_path = GetRealPath(full_path);
+  std::string real_path = vfs_.GetRealPath(full_path);
   if (real_path.empty()) {
     SetSyncError(reply, INVALID_VALUES_ERR);
     return;
@@ -1320,7 +1185,7 @@ void FilesystemInstance::HandleFileGetURI(const picojson::value& msg,
   }
   free(real_path_c);
 
-  std::string uri_path = JoinPath("file:/", full_path);
+  std::string uri_path = VirtualFS::JoinPath("file:/", full_path);
 
   SetSyncSuccess(reply, uri_path);
 }
@@ -1336,14 +1201,14 @@ void FilesystemInstance::HandleFileResolve(const picojson::value& msg,
     return;
   }
 
-  std::string full_path = JoinPath(msg.get("fullPath").to_str(),
-                                   msg.get("relativeFilePath").to_str());
+  std::string full_path = VirtualFS::JoinPath(msg.get("fullPath").to_str(),
+      msg.get("relativeFilePath").to_str());
   if (full_path.empty()) {
     SetSyncError(reply, INVALID_VALUES_ERR);
     return;
   }
 
-  std::string real_path = GetRealPath(full_path);
+  std::string real_path = vfs_.GetRealPath(full_path);
   if (real_path.empty()) {
     SetSyncError(reply, INVALID_VALUES_ERR);
     return;
@@ -1366,7 +1231,7 @@ void FilesystemInstance::HandleFileStat(const picojson::value& msg,
     return;
   }
 
-  std::string real_path = GetRealPath(msg.get("fullPath").to_str());
+  std::string real_path = vfs_.GetRealPath(msg.get("fullPath").to_str());
   if (real_path.empty()) {
     SetSyncError(reply, INVALID_VALUES_ERR);
     return;
@@ -1389,7 +1254,7 @@ void FilesystemInstance::HandleFileStat(const picojson::value& msg,
   o["isDirectory"] = picojson::value(is_directory);
   if (is_directory)
     o["length"] = picojson::value(
-        static_cast<double>(get_dir_entry_count(real_path.c_str())));
+        static_cast<double>(VirtualFS::GetDirEntryCount(real_path.c_str())));
 
 
   picojson::value v(o);
@@ -1622,118 +1487,17 @@ void FilesystemInstance::ReadText(std::fstream* file, size_t num_chars,
   return;
 }
 
-std::string FilesystemInstance::GetRealPath(const std::string& fullPath) {
-  std::size_t pos = fullPath.find_first_of('/');
-  std::string virtual_root = fullPath;
-
-  if (pos != std::string::npos) {
-    virtual_root = fullPath.substr(0, pos);
-  }
-
-  Storages::const_iterator it = storages_.find(virtual_root);
-
-  if (it == storages_.end())
-    return std::string();
-
-  if (pos != std::string::npos)
-    return it->second.GetFullPath() + fullPath.substr(pos);
-
-  return it->second.GetFullPath();
-}
-
-void FilesystemInstance::AddInternalStorage(
-    const std::string& label, const std::string& path) {
-  if (makePath(path))
-    storages_.insert(SorageLabelPair(label,
-                                     Storage(-1,
-                                             Storage::STORAGE_TYPE_INTERNAL,
-                                             Storage::STORAGE_STATE_MOUNTED,
-                                             path)));
-}
-
-void FilesystemInstance::AddStorage(int id,
-                                   storage_type_e type,
-                                   storage_state_e state,
-                                   const std::string& path) {
-  std::string label;
-  if (type == STORAGE_TYPE_INTERNAL)
-    label = kInternalStorage + std::to_string(id);
-  else if (type == STORAGE_TYPE_EXTERNAL)
-    label = kRemovableStorage + std::to_string(id);
-
-  storages_.insert(SorageLabelPair(label,
-                                   Storage(id,
-                                           type,
-                                           state,
-                                           path)));
-  if (std::find(watched_storages_.begin(),
-                watched_storages_.end(), id) != watched_storages_.end()) {
-    watched_storages_.push_back(id);
-    storage_set_state_changed_cb(id, OnStorageStateChanged, this);
-  }
-}
-
-void FilesystemInstance::NotifyStorageStateChanged(int id,
-                                                  storage_state_e state) {
-  for (Storages::iterator it = storages_.begin(); it != storages_.end(); ++it) {
-    if (it->second.GetId() == id) {
-      it->second.SetState(state);
-      picojson::object reply;
-      reply["storage"] = picojson::value(it->second.toJSON(it->first));
-      reply["cmd"] = picojson::value("storageChanged");
-      picojson::value value(reply);
-      PostMessage(value.serialize().c_str());
-      break;
-    }
-  }
+void FilesystemInstance::NotifyStorageStateChanged(const std::string& label,
+    Storage storage) {
+  picojson::object reply;
+  reply["storage"] = picojson::value(StorageToJSON(storage, label));
+  reply["cmd"] = picojson::value("storageChanged");
+  picojson::value value(reply);
+  PostMessage(value.serialize().c_str());
 }
 
-bool FilesystemInstance::OnStorageDeviceSupported(
-    int id, storage_type_e type, storage_state_e state,
-    const char* path, void* user_data) {
-  reinterpret_cast<FilesystemInstance*>(user_data)->AddStorage(
-      id, type, state, path);
-  return true;
-}
-
-void FilesystemInstance::OnStorageStateChanged(
-    int id, storage_state_e state, void* user_data) {
+void FilesystemInstance::OnStorageStateChanged(const std::string& label,
+    Storage storage, void* user_data) {
   reinterpret_cast<FilesystemInstance*>(user_data)->NotifyStorageStateChanged(
-      id, state);
-}
-
-FilesystemInstance::Storage::Storage(
-    int id, int type, int state, const std::string& fullpath)
-  : id_(id),
-    type_(type),
-    state_(state),
-    fullpath_(fullpath) { }
-
-picojson::object FilesystemInstance::Storage::toJSON(
-    const std::string& label) const {
-  picojson::object storage_object;
-  storage_object["label"] = picojson::value(label);
-  storage_object["type"] = picojson::value(type());
-  storage_object["state"] = picojson::value(state());
-  return storage_object;
-}
-
-std::string FilesystemInstance::Storage::type() const {
-  return (type_ == Storage::STORAGE_TYPE_INTERNAL) ? kStorageTypeInternal :
-      kStorageTypeExternal;
-}
-
-std::string FilesystemInstance::Storage::state() const {
-  switch (state_) {
-  case Storage::STORAGE_STATE_MOUNTED:
-  case Storage::STORAGE_STATE_MOUNTED_READONLY:
-    return kStorageStateMounted;
-  case Storage::STORAGE_STATE_REMOVED:
-    return kStorageStateRemoved;
-  case Storage::STORAGE_STATE_UNMOUNTABLE:
-    return kStorageStateUnmountable;
-  default:
-    assert(!"Not reached");
-  }
-  return std::string();
+      label, storage);
 }
index 19be05d..0b4d6b5 100644 (file)
@@ -5,18 +5,16 @@
 #ifndef FILESYSTEM_FILESYSTEM_INSTANCE_H_
 #define FILESYSTEM_FILESYSTEM_INSTANCE_H_
 
-#include <app_storage.h>
-
-#include <set>
-#include <string>
-#include <map>
 #include <fstream>
 #include <iostream>
+#include <map>
+#include <set>
+#include <string>
 #include <utility>
-#include <vector>
 
 #include "common/extension.h"
 #include "common/picojson.h"
+#include "common/virtual_fs.h"
 #include "tizen/tizen.h"
 
 class FilesystemInstance : public common::Instance {
@@ -25,43 +23,11 @@ class FilesystemInstance : public common::Instance {
   ~FilesystemInstance();
 
   // common::Instance implementation
+  void Initialize();
   void HandleMessage(const char* message);
   void HandleSyncMessage(const char* message);
 
  private:
-  class Storage {
-   public:
-    /* Mapped to storage_type_e */
-    enum StorageType {
-      STORAGE_TYPE_INTERNAL,
-      STORAGE_TYPE_EXTERNAL,
-    };
-
-    /* Mapped to storage_state_e */
-    enum StorageState {
-      STORAGE_STATE_UNMOUNTABLE = -2,
-      STORAGE_STATE_REMOVED = -1,
-      STORAGE_STATE_MOUNTED = 0,
-      STORAGE_STATE_MOUNTED_READONLY = 1,
-    };
-
-    Storage(int id, int type, int state, const std::string& fullpath);
-
-    picojson::object toJSON(const std::string& label) const;
-
-    std::string type() const;
-    std::string state() const;
-    int GetId() const { return id_; }
-    const std::string& GetFullPath() const { return fullpath_; }
-    void SetState(int state) { state_ = state; }
-
-   private:
-    int id_;
-    int type_;
-    int state_;
-    std::string fullpath_;
-  };
-
   /* Asynchronous messages */
   void HandleFileSystemManagerResolve(const picojson::value& msg);
   void HandleFileSystemManagerGetStorage(const picojson::value& msg);
@@ -112,24 +78,15 @@ class FilesystemInstance : public common::Instance {
   void SetSyncSuccess(std::string& reply, std::string& output);
   void SetSyncSuccess(std::string& reply, picojson::value& output);
 
-  std::string GetRealPath(const std::string& fullPath);
-  void AddInternalStorage(const std::string& label, const std::string& path);
-  void AddStorage(int storage, storage_type_e type, storage_state_e state,
-      const std::string& path);
-  void NotifyStorageStateChanged(int id, storage_state_e state);
-  static bool OnStorageDeviceSupported(int id, storage_type_e type,
-      storage_state_e state, const char *path, void *user_data);
-  static void OnStorageStateChanged(int id, storage_state_e state,
-      void *user_data);
+  void NotifyStorageStateChanged(const std::string& label, Storage storage);
+  static void OnStorageStateChanged(const std::string& label, Storage storage,
+      void* user_data);
 
   typedef std::tuple<std::ios_base::openmode, std::fstream*,
       std::string> FStream;
   typedef std::map<unsigned int, FStream> FStreamMap;
   FStreamMap fstream_map_;
-  typedef std::map<std::string, Storage> Storages;
-  typedef std::pair<std::string, Storage> SorageLabelPair;
-  Storages storages_;
-  std::vector<int> watched_storages_;
+  VirtualFS vfs_;
 };
 
 #endif  // FILESYSTEM_FILESYSTEM_INSTANCE_H_