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