[Release] wrt-installer_0.1.47
[framework/web/wrt-installer.git] / src / jobs / widget_install / task_file_manipulation.cpp
index 5d981ea..4e25780 100644 (file)
@@ -19,7 +19,9 @@
  * @version 1.0
  * @brief   Implementation file for installer task database updating
  */
+#include <unistd.h>
 #include <sys/stat.h>
+#include <dirent.h>
 #include <widget_install/task_file_manipulation.h>
 #include <widget_install/job_widget_install.h>
 #include <widget_install/widget_install_errors.h>
 #include <dpl/foreach.h>
 #include <dpl/log/log.h>
 #include <dpl/assert.h>
+#include <dpl/errno_string.h>
+#include <dpl/utils/folder_size.h>
+#include <dpl/wrt-dao-ro/global_config.h>
 #include <string>
+#include <fstream>
+#include <widget_install_to_external.h>
 
 #define WEBAPP_DEFAULT_UID  5000
 #define WEBAPP_DEFAULT_GID  5000
@@ -40,27 +47,129 @@ const mode_t SHARE_MODE = 0705;
 
 using namespace WrtDB;
 
+namespace {
+const char* GLIST_RES_DIR = "res";
+
+bool _FolderCopy(std::string source, std::string dest)
+{
+    DIR* dir = opendir(source.c_str());
+    if (NULL == dir) {
+        return false;
+    }
+
+    struct dirent dEntry;
+    struct dirent *dEntryResult;
+    int return_code;
+
+    do {
+        struct stat statInfo;
+        return_code = readdir_r(dir, &dEntry, &dEntryResult);
+        if (dEntryResult != NULL && return_code == 0) {
+            std::string fileName = dEntry.d_name;
+            std::string fullName = source + "/" + fileName;
+
+            if (stat(fullName.c_str(), &statInfo) != 0) {
+                closedir(dir);
+                return false;
+            }
+
+            if (S_ISDIR(statInfo.st_mode)) {
+                if (("." == fileName) || (".." == fileName)) {
+                    continue;
+                }
+                std::string destFolder = dest + "/" + fileName;
+                WrtUtilMakeDir(destFolder);
+
+                if (!_FolderCopy(fullName, destFolder)) {
+                    closedir(dir);
+                    return false;
+                }
+            }
+
+            std::string destFile = dest + "/" + fileName;
+            std::ifstream infile(fullName);
+            std::ofstream outfile(destFile);
+            outfile << infile.rdbuf();
+            outfile.close();
+            infile.close();
+        }
+    } while (dEntryResult != NULL && return_code == 0);
+    closedir(dir);
+    return true;
+}
+
+void changeOwnerForDirectory(std::string storagePath) {
+    if (euidaccess(storagePath.c_str(), F_OK) != 0) {
+        if (!WrtUtilMakeDir(storagePath, PRIVATE_STORAGE_MODE)) {
+            LogError("Failed to create directory for private storage");
+            ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed,
+                     "Failed to create directory for private storage");
+        }
+        // '5000' is default uid, gid for applications.
+        // So installed applications should be launched as process of uid
+        // '5000'.
+        // the process can access private directory 'data' of itself.
+        if (chown(storagePath.c_str(),
+                  WEBAPP_DEFAULT_UID,
+                  WEBAPP_DEFAULT_GID) != 0)
+        {
+            ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed,
+                     "Chown to invaild user");
+        }
+    } else if (euidaccess(storagePath.c_str(), W_OK | R_OK | X_OK) == 0) {
+        LogInfo("Private storage already exists.");
+        // Even if private directory already is created, private dircetory
+        // should change owner.
+        if (chown(storagePath.c_str(),
+                  WEBAPP_DEFAULT_UID,
+                  WEBAPP_DEFAULT_GID) != 0)
+        {
+            ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed,
+                     "Chown to invaild user");
+        }
+        if (chmod(storagePath.c_str(), PRIVATE_STORAGE_MODE) != 0) {
+            ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed,
+                     "chmod to 0700");
+        }
+    } else {
+        ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed,
+                 "No access to private storage.");
+    }
+}
+}
+
 namespace Jobs {
 namespace WidgetInstall {
 TaskFileManipulation::TaskFileManipulation(InstallerContext& context) :
     DPL::TaskDecl<TaskFileManipulation>(this),
-    m_context(context)
+    m_context(context),
+    m_extHandle(NULL)
 {
-    AddStep(&TaskFileManipulation::StepCreateDirs);
-    AddStep(&TaskFileManipulation::StepCreatePrivateStorageDir);
-    AddStep(&TaskFileManipulation::StepCreateShareDir);
-    AddStep(&TaskFileManipulation::StepRenamePath);
+    if (INSTALL_LOCATION_TYPE_EXTERNAL !=
+            m_context.locationType)
+    {
+        AddStep(&TaskFileManipulation::StepCreateDirs);
+        AddStep(&TaskFileManipulation::StepCreatePrivateStorageDir);
+        if (m_context.widgetConfig.packagingType !=
+            WrtDB::PKG_TYPE_DIRECTORY_WEB_APP)
+        {
+            AddStep(&TaskFileManipulation::StepRenamePath);
+            AddAbortStep(&TaskFileManipulation::StepAbortRenamePath);
+        }
+        AddStep(&TaskFileManipulation::StepLinkForPreload);
 
-    AddAbortStep(&TaskFileManipulation::StepAbortRenamePath);
+    } else {
+        AddStep(&TaskFileManipulation::StepPrepareExternalDir);
+        AddStep(&TaskFileManipulation::StepInstallToExternal);
+        AddStep(&TaskFileManipulation::StepCreatePrivateStorageDir);
+
+        AddAbortStep(&TaskFileManipulation::StepAbortCreateExternalDir);
+    }
 }
 
 void TaskFileManipulation::StepCreateDirs()
 {
     std::string widgetPath;
-    DPL::OptionalString pkgname = m_context.widgetConfig.pkgname;
-    if (pkgname.IsNull()) {
-        ThrowMsg(Exceptions::InternalError, "No Package name exists.");
-    }
 
     widgetPath = m_context.locations->getPackageInstallationDir();
 
@@ -77,6 +186,12 @@ void TaskFileManipulation::StepCreateDirs()
         LogDebug("Create resource directory");
         WrtUtilMakeDir(widgetBinPath);
         WrtUtilMakeDir(widgetSrcPath);
+        if (m_context.job->getInstallerStruct().m_installMode
+                == InstallMode::INSTALL_MODE_PRELOAD)
+        {
+            std::string userWidgetDir = m_context.locations->getUserDataRootDir();
+            WrtUtilMakeDir(userWidgetDir);
+        }
     }
 
     m_context.job->UpdateProgress(
@@ -87,95 +202,14 @@ void TaskFileManipulation::StepCreateDirs()
 void TaskFileManipulation::StepCreatePrivateStorageDir()
 {
     std::string storagePath = m_context.locations->getPrivateStorageDir();
-
-    if (euidaccess(storagePath.c_str(), F_OK) != 0) {
-        if(!WrtUtilMakeDir(storagePath, PRIVATE_STORAGE_MODE)){
-            LogError("Failed to create directory for private storage");
-            ThrowMsg(Exceptions::InternalError,
-                    "Failed to create directory for private storage");
-        }
-        // '5000' is default uid, gid for applications.
-        // So installed applications should be launched as process of uid '5000'.
-        // the process can access private directory 'data' of itself.
-        if(chown(storagePath.c_str(),
-                 WEBAPP_DEFAULT_UID,
-                 WEBAPP_DEFAULT_GID) != 0)
-        {
-            ThrowMsg(Exceptions::InternalError,
-                 "Chown to invaild user");
-        }
-    } else if (euidaccess(storagePath.c_str(), W_OK | R_OK | X_OK) == 0) {
-        LogInfo("Private storage already exists.");
-        // Even if private directory already is created, private dircetory
-        // should change owner.
-        if(chown(storagePath.c_str(),
-                 WEBAPP_DEFAULT_UID,
-                 WEBAPP_DEFAULT_GID) != 0)
-        {
-            ThrowMsg(Exceptions::InternalError,
-                 "Chown to invaild user");
-        }
-        if(chmod(storagePath.c_str(), PRIVATE_STORAGE_MODE) != 0) {
-            ThrowMsg(Exceptions::InternalError,
-                 "chmod to 0700");
-        }
-
-    } else {
-        ThrowMsg(Exceptions::InternalError,
-                 "No access to private storage.");
-    }
-}
-
-void TaskFileManipulation::StepCreateShareDir()
-{
-    std::string sharePath = m_context.locations->getShareDir();
-
-    if (euidaccess(sharePath.c_str(), F_OK) != 0) {
-        if(!WrtUtilMakeDir(sharePath, SHARE_MODE)){
-            LogError("Failed to create directory for share");
-            ThrowMsg(Exceptions::InternalError,
-                    "Failed to create directory for share");
-        }
-        // '5000' is default uid, gid for applications.
-        // So installed applications should be launched as process of uid '5000'.
-        // the process can access private directory 'data' of itself.
-        if(chown(sharePath.c_str(),
-                 WEBAPP_DEFAULT_UID,
-                 WEBAPP_DEFAULT_GID) != 0)
-        {
-            ThrowMsg(Exceptions::InternalError,
-                 "Chown to invaild user");
-        }
-    } else if (euidaccess(sharePath.c_str(), W_OK | R_OK | X_OK) == 0) {
-        LogInfo("Share directory already exists.");
-        // Even if share directory already is created, share dircetory
-        // should change owner.
-        if(chown(sharePath.c_str(),
-                 WEBAPP_DEFAULT_UID,
-                 WEBAPP_DEFAULT_GID) != 0)
-        {
-            ThrowMsg(Exceptions::InternalError,
-                 "Chown to invaild user");
-        }
-        if(chmod(sharePath.c_str(), SHARE_MODE) != 0) {
-            ThrowMsg(Exceptions::InternalError,
-                 "chmod to 0700");
-        }
-
-    } else {
-        ThrowMsg(Exceptions::InternalError,
-                 "No access to private storage.");
-    }
-
+    LogDebug("Create private storage directory : " <<
+            m_context.locations->getPrivateStorageDir());
+    changeOwnerForDirectory(storagePath);
 }
 
 void TaskFileManipulation::StepRenamePath()
 {
     std::string instDir;
-    DPL::OptionalString pkgname = m_context.widgetConfig.pkgname;
-    if (pkgname.IsNull()) {
-        ThrowMsg(Exceptions::InternalError, "No Package name exists.");
-    }
 
     if (m_context.widgetConfig.packagingType == PKG_TYPE_HYBRID_WEB_APP) {
         instDir = m_context.locations->getPackageInstallationDir();
@@ -186,37 +220,183 @@ void TaskFileManipulation::StepRenamePath()
     LogDebug("Copy file from temp directory to " << instDir);
     if (!WrtUtilRemove(instDir)) {
         ThrowMsg(Exceptions::RemovingFolderFailure,
-                "Error occurs during removing existing folder");
+                 "Error occurs during removing existing folder");
     }
 
-    if (!(rename(m_context.locations->getTemporaryPackageDir().c_str(), instDir.c_str()) == 0)) {
-        ThrowMsg(Exceptions::UnknownError,
-                "Error occurs during renaming widget folder");
+    if (!(rename(m_context.locations->getTemporaryPackageDir().c_str(),
+                 instDir.c_str()) == 0))
+    {
+        ThrowMsg(Exceptions::FileOperationFailed,
+                 "Error occurs during renaming widget folder");
     }
     m_context.job->UpdateProgress(
         InstallerContext::INSTALL_RENAME_PATH,
         "Widget Rename path Finished");
 }
 
+void TaskFileManipulation::StepLinkForPreload()
+{
+    if (m_context.job->getInstallerStruct().m_installMode
+            == InstallMode::INSTALL_MODE_PRELOAD)
+    {
+        std::string srcDir = m_context.locations->getUserDataRootDir() +
+            WrtDB::GlobalConfig::GetWidgetSrcPath();
+
+        if (0 != access(srcDir.c_str(), F_OK)) {
+            LogDebug("Make symbolic name for preaload app" <<
+                    m_context.locations->getSourceDir() << " to " << srcDir);
+            std::string resDir = m_context.locations->getUserDataRootDir() +
+                "/res";
+
+            WrtUtilMakeDir(resDir);
+            if (symlink(m_context.locations->getSourceDir().c_str(), srcDir.c_str()) != 0)
+            {
+                int error = errno;
+                if (error)
+                    LogPedantic("Failed to make a symbolic name for a file "
+                            << "[" <<  DPL::GetErrnoString(error) << "]");
+                ThrowMsg(Exceptions::FileOperationFailed,
+                        "Symbolic link creating is not done.");
+            }
+        }
+
+        /* link for data directory */
+        std::string storagePath = m_context.locations->getPrivateStorageDir();
+        std::string dataDir = m_context.locations->getPackageInstallationDir() +
+            "/" + WrtDB::GlobalConfig::GetWidgetPrivateStoragePath();
+        if (0 != access(dataDir.c_str(), F_OK)) {
+            LogDebug("Make symbolic name for preaload app " <<
+                    storagePath << " to " << dataDir);
+
+            if (symlink(storagePath.c_str(), dataDir.c_str()) != 0)
+            {
+                int error = errno;
+                if (error)
+                    LogPedantic("Failed to make a symbolic name for a file "
+                            << "[" <<  DPL::GetErrnoString(error) << "]");
+                ThrowMsg(Exceptions::FileOperationFailed,
+                        "Symbolic link creating is not done.");
+            }
+            changeOwnerForDirectory(dataDir);
+        }
+
+        if (m_context.widgetConfig.packagingType != PKG_TYPE_HYBRID_WEB_APP) {
+            std::string widgetBinPath = m_context.locations->getBinaryDir();
+            std::string userBinPath = m_context.locations->getUserBinaryDir();
+            LogDebug("Make symbolic link for preload app " << widgetBinPath <<
+                    " to " << userBinPath);
+            if (symlink(widgetBinPath.c_str(), userBinPath.c_str()) != 0)
+            {
+                int error = errno;
+                if (error)
+                    LogPedantic("Failed to make a symbolic name for a file "
+                            << "[" <<  DPL::GetErrnoString(error) << "]");
+                ThrowMsg(Exceptions::FileOperationFailed,
+                        "Symbolic link creating is not done.");
+            }
+
+        }
+    }
+}
+
 void TaskFileManipulation::StepAbortRenamePath()
 {
     LogDebug("[Rename Widget Path] Aborting.... (Rename path)");
     std::string widgetPath;
-    if (m_context.widgetConfig.packagingType == PKG_TYPE_HYBRID_WEB_APP) {
+    if (m_context.widgetConfig.packagingType != PKG_TYPE_HYBRID_WEB_APP) {
         widgetPath = m_context.locations->getPackageInstallationDir();
-    } else {
-        widgetPath = m_context.locations->getSourceDir();
+        if (!WrtUtilRemove(widgetPath)) {
+            ThrowMsg(Exceptions::RemovingFolderFailure,
+                     "Error occurs during removing existing folder");
+        }
+        // Remove user data directory if preload web app.
+        std::string userData = m_context.locations->getUserDataRootDir();
+        if (0 == access(userData.c_str(), F_OK)) {
+            WrtUtilRemove(userData);
+        }
+
+    }
+    LogDebug("Rename widget path sucessful!");
+}
+
+void TaskFileManipulation::StepPrepareExternalDir()
+{
+    LogDebug("Step prepare to install in exernal directory");
+    Try {
+        std::string pkgid =
+            DPL::ToUTF8String(m_context.widgetConfig.tzPkgid);
+
+        WidgetInstallToExtSingleton::Instance().initialize(pkgid);
+
+        size_t totalSize =
+            Utils::getFolderSize(m_context.locations->getTemporaryPackageDir());
+
+        int folderSize = (int)(totalSize / (1024 * 1024)) + 1;
+
+        GList *list = NULL;
+        app2ext_dir_details* dirDetail = NULL;
+
+        dirDetail = (app2ext_dir_details*) calloc(1,
+                sizeof(
+                    app2ext_dir_details));
+        if (NULL == dirDetail) {
+            ThrowMsg(Exceptions::ErrorExternalInstallingFailure,
+                    "error in app2ext");
+        }
+        dirDetail->name = strdup(GLIST_RES_DIR);
+        dirDetail->type = APP2EXT_DIR_RO;
+        list = g_list_append(list, dirDetail);
+
+        if (m_context.isUpdateMode) {
+            WidgetInstallToExtSingleton::Instance().preUpgrade(list,
+                                                               folderSize);
+        } else {
+            WidgetInstallToExtSingleton::Instance().preInstallation(list,
+                                                                    folderSize);
+        }
+        free(dirDetail);
+        g_list_free(list);
+
+        /* make bin directory */
+        std::string widgetBinPath = m_context.locations->getBinaryDir();
+        WrtUtilMakeDir(widgetBinPath);
     }
-    struct stat fileInfo;
-    if (stat(widgetPath.c_str(), &fileInfo) != 0) {
-        LogError("Failed to get widget file path : " << widgetPath);
-        return;
+    Catch(WidgetInstallToExt::Exception::ErrorInstallToExt)
+    {
+        ReThrowMsg(Exceptions::ErrorExternalInstallingFailure,
+                   "Error during \
+                create external folder ");
     }
+}
 
-    if (!(rename(widgetPath.c_str(), m_context.locations->getTemporaryPackageDir().c_str()) == 0)) {
-        LogError("Failed to rename");
+void TaskFileManipulation::StepInstallToExternal()
+{
+    LogDebug("StepInstallExternal");
+    if (!WrtUtilMakeDir(m_context.locations->getSourceDir())) {
+        ThrowMsg(Exceptions::ErrorExternalInstallingFailure,
+                 "To make src \
+                directory failed");
     }
-    LogDebug("Rename widget path sucessful!");
+
+    LogDebug("Resource move to external storage " <<
+             m_context.locations->getSourceDir());
+    if (!_FolderCopy(m_context.locations->getTemporaryPackageDir(),
+                     m_context.locations->getSourceDir()))
+    {
+        ThrowMsg(Exceptions::ErrorExternalInstallingFailure,
+                 "Error occurs during renaming widget folder");
+    }
+}
+
+void TaskFileManipulation::StepAbortCreateExternalDir()
+{
+    LogError("Abort StepAbortCreateExternalDir");
+    if (m_context.isUpdateMode) {
+        WidgetInstallToExtSingleton::Instance().postUpgrade(false);
+    } else {
+        WidgetInstallToExtSingleton::Instance().postInstallation(false);
+    }
+    WidgetInstallToExtSingleton::Instance().deinitialize();
 }
 } //namespace WidgetInstall
 } //namespace Jobs