Fixed update web app
[framework/web/wrt-installer.git] / src / jobs / widget_install / task_file_manipulation.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 /**
17  * @file    task_db_update.cpp
18  * @author  Lukasz Wrzosek(l.wrzosek@samsung.com)
19  * @version 1.0
20  * @brief   Implementation file for installer task database updating
21  */
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <dirent.h>
25 #include <widget_install/task_file_manipulation.h>
26 #include <widget_install/job_widget_install.h>
27 #include <widget_install/widget_install_errors.h>
28 #include <widget_install/widget_install_context.h>
29 #include <widget_install/directory_api.h>
30 #include <dpl/utils/wrt_utility.h>
31 #include <dpl/foreach.h>
32 #include <dpl/log/log.h>
33 #include <dpl/assert.h>
34 #include <dpl/errno_string.h>
35 #include <dpl/utils/folder_size.h>
36 #include <dpl/wrt-dao-ro/global_config.h>
37 #include <string>
38 #include <fstream>
39 #include <widget_install_to_external.h>
40
41 #define WEBAPP_DEFAULT_UID  5000
42 #define WEBAPP_DEFAULT_GID  5000
43
44 namespace {
45 const mode_t PRIVATE_STORAGE_MODE = 0700;
46 const mode_t SHARE_MODE = 0705;
47 }
48
49 using namespace WrtDB;
50
51 namespace {
52 const char* GLIST_RES_DIR = "res";
53
54 bool _FolderCopy(std::string source, std::string dest)
55 {
56     DIR* dir = opendir(source.c_str());
57     if (NULL == dir) {
58         return false;
59     }
60
61     struct dirent dEntry;
62     struct dirent *dEntryResult;
63     int return_code;
64
65     do {
66         struct stat statInfo;
67         return_code = readdir_r(dir, &dEntry, &dEntryResult);
68         if (dEntryResult != NULL && return_code == 0) {
69             std::string fileName = dEntry.d_name;
70             std::string fullName = source + "/" + fileName;
71
72             if (stat(fullName.c_str(), &statInfo) != 0) {
73                 closedir(dir);
74                 return false;
75             }
76
77             if (S_ISDIR(statInfo.st_mode)) {
78                 if (("." == fileName) || (".." == fileName)) {
79                     continue;
80                 }
81                 std::string destFolder = dest + "/" + fileName;
82                 WrtUtilMakeDir(destFolder);
83
84                 if (!_FolderCopy(fullName, destFolder)) {
85                     closedir(dir);
86                     return false;
87                 }
88             }
89
90             std::string destFile = dest + "/" + fileName;
91             std::ifstream infile(fullName);
92             std::ofstream outfile(destFile);
93             outfile << infile.rdbuf();
94             outfile.close();
95             infile.close();
96         }
97     } while (dEntryResult != NULL && return_code == 0);
98     closedir(dir);
99     return true;
100 }
101
102 void changeOwnerForDirectory(std::string storagePath) {
103     if (euidaccess(storagePath.c_str(), F_OK) != 0) {
104         if (!WrtUtilMakeDir(storagePath, PRIVATE_STORAGE_MODE)) {
105             LogError("Failed to create directory for private storage");
106             ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed,
107                      "Failed to create directory for private storage");
108         }
109         // '5000' is default uid, gid for applications.
110         // So installed applications should be launched as process of uid
111         // '5000'.
112         // the process can access private directory 'data' of itself.
113         if (chown(storagePath.c_str(),
114                   WEBAPP_DEFAULT_UID,
115                   WEBAPP_DEFAULT_GID) != 0)
116         {
117             ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed,
118                      "Chown to invaild user");
119         }
120     } else if (euidaccess(storagePath.c_str(), W_OK | R_OK | X_OK) == 0) {
121         LogInfo("Private storage already exists.");
122         // Even if private directory already is created, private dircetory
123         // should change owner.
124         if (chown(storagePath.c_str(),
125                   WEBAPP_DEFAULT_UID,
126                   WEBAPP_DEFAULT_GID) != 0)
127         {
128             ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed,
129                      "Chown to invaild user");
130         }
131         if (chmod(storagePath.c_str(), PRIVATE_STORAGE_MODE) != 0) {
132             ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed,
133                      "chmod to 0700");
134         }
135     } else {
136         ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed,
137                  "No access to private storage.");
138     }
139 }
140 }
141
142 namespace Jobs {
143 namespace WidgetInstall {
144 TaskFileManipulation::TaskFileManipulation(InstallerContext& context) :
145     DPL::TaskDecl<TaskFileManipulation>(this),
146     m_context(context),
147     m_extHandle(NULL)
148 {
149     if (INSTALL_LOCATION_TYPE_EXTERNAL !=
150             m_context.locationType)
151     {
152         AddStep(&TaskFileManipulation::StepCreateDirs);
153         if (m_context.widgetConfig.packagingType !=
154             WrtDB::PKG_TYPE_DIRECTORY_WEB_APP)
155         {
156             AddStep(&TaskFileManipulation::StepRenamePath);
157             AddAbortStep(&TaskFileManipulation::StepAbortRenamePath);
158         }
159         AddStep(&TaskFileManipulation::StepCreatePrivateStorageDir);
160         AddStep(&TaskFileManipulation::StepLinkForPreload);
161
162     } else {
163         AddStep(&TaskFileManipulation::StepPrepareExternalDir);
164         AddStep(&TaskFileManipulation::StepInstallToExternal);
165         AddStep(&TaskFileManipulation::StepCreatePrivateStorageDir);
166
167         AddAbortStep(&TaskFileManipulation::StepAbortCreateExternalDir);
168     }
169 }
170
171 void TaskFileManipulation::StepCreateDirs()
172 {
173     std::string widgetPath;
174
175     widgetPath = m_context.locations->getPackageInstallationDir();
176
177     std::string widgetBinPath = m_context.locations->getBinaryDir();
178     std::string widgetSrcPath = m_context.locations->getSourceDir();
179
180     WrtUtilMakeDir(widgetPath);
181
182     // If package type is widget with osp service, we don't need to make bin
183     // and src directory
184     if (m_context.widgetConfig.packagingType == PKG_TYPE_HYBRID_WEB_APP) {
185         LogDebug("Doesn't need to create resource directory");
186     } else {
187         LogDebug("Create resource directory");
188         WrtUtilMakeDir(widgetBinPath);
189         WrtUtilMakeDir(widgetSrcPath);
190         if (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD) {
191             std::string userWidgetDir = m_context.locations->getUserDataRootDir();
192             WrtUtilMakeDir(userWidgetDir);
193         }
194     }
195
196     m_context.job->UpdateProgress(
197         InstallerContext::INSTALL_DIR_CREATE,
198         "Widget Directory Created");
199 }
200
201 void TaskFileManipulation::StepCreatePrivateStorageDir()
202 {
203     std::string storagePath = m_context.locations->getPrivateStorageDir();
204     LogDebug("Create private storage directory : " <<
205             m_context.locations->getPrivateStorageDir());
206
207     if (m_context.isUpdateMode) { //update
208         std::string backData = m_context.locations->getBackupPrivateDir();
209         LogDebug("copy private storage " << backData << " to " << storagePath);
210         WrtUtilMakeDir(storagePath);
211         if (!DirectoryApi::DirectoryCopy(backData, storagePath)) {
212             LogError("Failed to rename " << backData << " to " << storagePath);
213             ThrowMsg(Exceptions::BackupFailed,
214                     "Error occurs copy private strage files");
215         }
216     }
217     changeOwnerForDirectory(storagePath);
218 }
219
220 void TaskFileManipulation::StepRenamePath()
221 {
222     std::string instDir;
223
224     if (m_context.widgetConfig.packagingType == PKG_TYPE_HYBRID_WEB_APP) {
225         instDir = m_context.locations->getPackageInstallationDir();
226     } else {
227         instDir = m_context.locations->getSourceDir();
228     }
229
230     LogDebug("Copy file from temp directory to " << instDir);
231     if (!WrtUtilRemove(instDir)) {
232         ThrowMsg(Exceptions::RemovingFolderFailure,
233                  "Error occurs during removing existing folder");
234     }
235
236     if (!(rename(m_context.locations->getTemporaryPackageDir().c_str(),
237                  instDir.c_str()) == 0))
238     {
239         ThrowMsg(Exceptions::FileOperationFailed,
240                  "Error occurs during renaming widget folder");
241     }
242     m_context.job->UpdateProgress(
243         InstallerContext::INSTALL_RENAME_PATH,
244         "Widget Rename path Finished");
245 }
246
247 void TaskFileManipulation::StepLinkForPreload()
248 {
249     if (m_context.mode.rootPath == InstallMode::RootPath::RO) {
250         std::string srcDir = m_context.locations->getUserDataRootDir() +
251             WrtDB::GlobalConfig::GetWidgetSrcPath();
252
253         if (0 != access(srcDir.c_str(), F_OK)) {
254             LogDebug("Make symbolic name for preaload app" <<
255                     m_context.locations->getSourceDir() << " to " << srcDir);
256             std::string resDir = m_context.locations->getUserDataRootDir() +
257                 "/res";
258
259             WrtUtilMakeDir(resDir);
260             if (symlink(m_context.locations->getSourceDir().c_str(), srcDir.c_str()) != 0)
261             {
262                 int error = errno;
263                 if (error)
264                     LogPedantic("Failed to make a symbolic name for a file "
265                             << "[" <<  DPL::GetErrnoString(error) << "]");
266                 ThrowMsg(Exceptions::FileOperationFailed,
267                         "Symbolic link creating is not done.");
268             }
269         }
270
271         /* link for data directory */
272         std::string storagePath = m_context.locations->getPrivateStorageDir();
273         std::string dataDir = m_context.locations->getPackageInstallationDir() +
274             "/" + WrtDB::GlobalConfig::GetWidgetPrivateStoragePath();
275         if (0 != access(dataDir.c_str(), F_OK)) {
276             LogDebug("Make symbolic name for preaload app " <<
277                     storagePath << " to " << dataDir);
278
279             if (symlink(storagePath.c_str(), dataDir.c_str()) != 0)
280             {
281                 int error = errno;
282                 if (error)
283                     LogPedantic("Failed to make a symbolic name for a file "
284                             << "[" <<  DPL::GetErrnoString(error) << "]");
285                 ThrowMsg(Exceptions::FileOperationFailed,
286                         "Symbolic link creating is not done.");
287             }
288             changeOwnerForDirectory(dataDir);
289         }
290
291         if (m_context.widgetConfig.packagingType != PKG_TYPE_HYBRID_WEB_APP) {
292             std::string widgetBinPath = m_context.locations->getBinaryDir();
293             std::string userBinPath = m_context.locations->getUserBinaryDir();
294             LogDebug("Make symbolic link for preload app " << widgetBinPath <<
295                     " to " << userBinPath);
296             if (symlink(widgetBinPath.c_str(), userBinPath.c_str()) != 0)
297             {
298                 int error = errno;
299                 if (error)
300                     LogPedantic("Failed to make a symbolic name for a file "
301                             << "[" <<  DPL::GetErrnoString(error) << "]");
302                 ThrowMsg(Exceptions::FileOperationFailed,
303                         "Symbolic link creating is not done.");
304             }
305
306         }
307     }
308 }
309
310 void TaskFileManipulation::StepAbortRenamePath()
311 {
312     LogDebug("[Rename Widget Path] Aborting.... (Rename path)");
313     std::string widgetPath;
314     if (m_context.widgetConfig.packagingType != PKG_TYPE_HYBRID_WEB_APP) {
315         widgetPath = m_context.locations->getPackageInstallationDir();
316         if (!WrtUtilRemove(widgetPath)) {
317             ThrowMsg(Exceptions::RemovingFolderFailure,
318                      "Error occurs during removing existing folder");
319         }
320         // Remove user data directory if preload web app.
321         std::string userData = m_context.locations->getUserDataRootDir();
322         if (0 == access(userData.c_str(), F_OK)) {
323             WrtUtilRemove(userData);
324         }
325
326     }
327     LogDebug("Rename widget path sucessful!");
328 }
329
330 void TaskFileManipulation::StepPrepareExternalDir()
331 {
332     LogDebug("Step prepare to install in exernal directory");
333     Try {
334         std::string pkgid =
335             DPL::ToUTF8String(m_context.widgetConfig.tzPkgid);
336
337         WidgetInstallToExtSingleton::Instance().initialize(pkgid);
338
339         size_t totalSize =
340             Utils::getFolderSize(m_context.locations->getTemporaryPackageDir());
341
342         int folderSize = (int)(totalSize / (1024 * 1024)) + 1;
343
344         GList *list = NULL;
345         app2ext_dir_details* dirDetail = NULL;
346
347         dirDetail = (app2ext_dir_details*) calloc(1,
348                 sizeof(
349                     app2ext_dir_details));
350         if (NULL == dirDetail) {
351             ThrowMsg(Exceptions::ErrorExternalInstallingFailure,
352                     "error in app2ext");
353         }
354         dirDetail->name = strdup(GLIST_RES_DIR);
355         dirDetail->type = APP2EXT_DIR_RO;
356         list = g_list_append(list, dirDetail);
357
358         if (m_context.isUpdateMode) {
359             WidgetInstallToExtSingleton::Instance().preUpgrade(list,
360                                                                folderSize);
361         } else {
362             WidgetInstallToExtSingleton::Instance().preInstallation(list,
363                                                                     folderSize);
364         }
365         free(dirDetail);
366         g_list_free(list);
367
368         /* make bin directory */
369         std::string widgetBinPath = m_context.locations->getBinaryDir();
370         WrtUtilMakeDir(widgetBinPath);
371     }
372     Catch(WidgetInstallToExt::Exception::ErrorInstallToExt)
373     {
374         ReThrowMsg(Exceptions::ErrorExternalInstallingFailure,
375                    "Error during \
376                 create external folder ");
377     }
378 }
379
380 void TaskFileManipulation::StepInstallToExternal()
381 {
382     LogDebug("StepInstallExternal");
383     if (!WrtUtilMakeDir(m_context.locations->getSourceDir())) {
384         ThrowMsg(Exceptions::ErrorExternalInstallingFailure,
385                  "To make src \
386                 directory failed");
387     }
388
389     LogDebug("Resource move to external storage " <<
390              m_context.locations->getSourceDir());
391     if (!_FolderCopy(m_context.locations->getTemporaryPackageDir(),
392                      m_context.locations->getSourceDir()))
393     {
394         ThrowMsg(Exceptions::ErrorExternalInstallingFailure,
395                  "Error occurs during renaming widget folder");
396     }
397 }
398
399 void TaskFileManipulation::StepAbortCreateExternalDir()
400 {
401     LogError("Abort StepAbortCreateExternalDir");
402     if (m_context.isUpdateMode) {
403         WidgetInstallToExtSingleton::Instance().postUpgrade(false);
404     } else {
405         WidgetInstallToExtSingleton::Instance().postInstallation(false);
406     }
407     WidgetInstallToExtSingleton::Instance().deinitialize();
408 }
409 } //namespace WidgetInstall
410 } //namespace Jobs