Refactoring to extract duplicate logic as a separate method
[platform/core/dotnet/launcher.git] / NativeLauncher / tool / tac_installer.cc
1 /*
2  * Copyright (c) 2022 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 #include "log.h"
18 #include "utils.h"
19 #include "db_manager.h"
20 #include "tac_common.h"
21 #include "tac_installer.h"
22 #include "tac_db.h"
23
24 #include <cstring>
25 #include <fstream>
26 #include <regex>
27 #include <sstream>
28 #include <vector>
29 #include <boost/filesystem.hpp>
30 #include <json/json.h>
31 #include <pkgmgr-info.h>
32
33 #ifdef  LOG_TAG
34 #undef  LOG_TAG
35 #endif
36 #define LOG_TAG "DOTNET_INSTALLER_PLUGIN"
37
38 #define __XSTR(x) #x
39 #define __STR(x) __XSTR(x)
40 static const char* __DOTNET_DIR = __STR(DOTNET_DIR);
41 #undef __STR
42 #undef __XSTR
43
44 static std::vector<std::string> nugetPackagesAssembliesSha;
45 static std::vector<std::string> tacDB;
46 static std::vector<std::string> createDirectories;
47 static std::vector<std::string> createLibraries;
48 static std::vector<std::string> updateTac;
49 static std::vector<std::string> updateTlc;
50 static tac_state tacState = TAC_STATE_NONE;
51 static bool tacPluginInstalled = false;
52 static bool tacPluginFinished = false;
53
54 static void createSHA256Info(std::string sha256Info, std::string nugetPackage)
55 {
56         std::ofstream ofs(sha256Info, std::ios::app);
57         int assembly_count = 0;
58         for (auto& npAssemblySha : nugetPackagesAssembliesSha) {
59                 std::string nuget_package_assembly = npAssemblySha.substr(0, npAssemblySha.rfind(':'));
60                 std::string nuget_package = nuget_package_assembly.substr(0, nuget_package_assembly.rfind(':'));
61                 std::string assembly = nuget_package_assembly.substr(nuget_package_assembly.rfind(':') + 1);
62                 std::string sha = npAssemblySha.substr(npAssemblySha.rfind(':') + 1);
63                 if (!strcmp(nuget_package.c_str(), nugetPackage.c_str())) {
64                         ofs << assembly << ":" << sha << std::endl;
65                         assembly_count++;
66                 }
67         }
68         ofs << assembly_count << std::endl;
69         ofs.close();
70 }
71
72 static bool compareSHA256Info(std::string sha256Info, std::string nugetPackage)
73 {
74         int compare_count = 0;
75         int assembly_count = 0;
76         std::string sha256_count = "0";
77         for (auto& npAssemblySha : nugetPackagesAssembliesSha) {
78                 std::string nuget_package_assembly = npAssemblySha.substr(0, npAssemblySha.rfind(':'));
79                 std::string nuget_package = nuget_package_assembly.substr(0, nuget_package_assembly.rfind(':'));
80                 std::string assembly = nuget_package_assembly.substr(nuget_package_assembly.rfind(':') + 1);
81                 std::string sha = npAssemblySha.substr(npAssemblySha.rfind(':') + 1);
82                 if (!strcmp(nuget_package.c_str(), nugetPackage.c_str())) {
83                         assembly_count++;
84                         std::ifstream ifs(sha256Info);
85                         std::string get_str;
86                         if (ifs.is_open()) {
87                                 while (getline(ifs, get_str)) {
88                                         if (!strcmp(get_str.c_str(), (assembly + ":" + sha).c_str())) {
89                                                 compare_count++;
90                                         }
91                                         sha256_count = get_str;
92                                 }
93                                 ifs.close();
94                         }
95                 }
96         }
97         if (!strcmp(std::to_string(assembly_count).c_str(), std::to_string(compare_count).c_str()) &&
98                 !strcmp(std::to_string(assembly_count).c_str(), sha256_count.c_str())) {
99                 _INFO("Same nuget : %s", nugetPackage.c_str());
100                 return true;
101         }
102         return false;
103 }
104
105 static bool copyAssemblyCreateSymlink(std::string binPath, std::string tacDir, std::string nugetPackage, bool isCreateTacDir)
106 {
107         std::string binNiPath = concatPath(binPath, APP_NI_SUB_DIR);
108         std::string tac_version_dir = concatPath(__DOTNET_DIR, nugetPackage);
109         bool nuget_restoration = false;
110         for (auto& npAssemblySha : nugetPackagesAssembliesSha) {
111                 std::string nuget_package_assembly = npAssemblySha.substr(0, npAssemblySha.rfind(':'));
112                 std::string nuget_package = nuget_package_assembly.substr(0, nuget_package_assembly.rfind(':'));
113                 std::string assembly = nuget_package_assembly.substr(nuget_package_assembly.rfind(':') + 1);
114                 if (!strcmp(nuget_package.c_str(), nugetPackage.c_str())) {
115                         if (exist(concatPath(binPath, assembly))) {
116                                 std::string niFile = changeExtension(assembly, ".dll", ".ni.dll");
117                                 bs::error_code error;
118                                 if (isCreateTacDir) {
119                                         if (!copyFile(concatPath(binPath, assembly), concatPath(tac_version_dir, assembly))) {
120                                                 _ERR("Failed to copy of %s", assembly.c_str());
121                                                 nuget_restoration = true;
122                                                 break;
123                                         }
124                                         if (exist(binNiPath) && !copyFile(concatPath(binNiPath, niFile), concatPath(tac_version_dir, niFile))) {
125                                                 _ERR("Failed to copy of %s", niFile.c_str());
126                                         }
127                                 }
128                                 bf::create_symlink(concatPath(tac_version_dir, assembly), concatPath(tacDir, assembly), error);
129                                 if (error) {
130                                         _ERR("Failed to create symlink %s file", concatPath(tacDir, assembly).c_str());
131                                         nuget_restoration = true;
132                                         break;
133                                 }
134                                 if (exist(concatPath(tac_version_dir, niFile)) && exist(binNiPath)) {
135                                         bf::create_symlink(concatPath(tac_version_dir, niFile), concatPath(tacDir, niFile), error);
136                                         if (error) {
137                                                 _ERR("Failed to create symlink %s file", concatPath(tacDir, niFile).c_str());
138                                         }
139                                 }
140
141                                 copySmackAndOwnership(tacDir, concatPath(tacDir, assembly), true);
142
143                                 if (!removeFile(concatPath(binPath, assembly))) {
144                                         _ERR("Failed to remove of %s", assembly.c_str());
145                                         nuget_restoration = true;
146                                         break;
147                                 }
148                                 if (!removeFile(concatPath(binNiPath, niFile))) {
149                                         _ERR("Failed to remove of %s", niFile.c_str());
150                                 }
151                         }
152                 }
153         }
154
155         if (nuget_restoration) {
156                 for (auto& npAssemblySha : nugetPackagesAssembliesSha) {
157                         std::string nuget_package_assembly = npAssemblySha.substr(0, npAssemblySha.rfind(':'));
158                         std::string nuget_package = nuget_package_assembly.substr(0, nuget_package_assembly.rfind(':'));
159                         std::string assembly = nuget_package_assembly.substr(nuget_package_assembly.rfind(':') + 1);
160                         if (!strcmp(nuget_package.c_str(), nugetPackage.c_str())) {
161                                 copyFile(concatPath(tac_version_dir, assembly), concatPath(binPath, assembly));
162                                 copySmackAndOwnership(binPath, concatPath(binPath, assembly));
163                                 removeFile(concatPath(tacDir, assembly));
164                         }
165                 }
166         }
167
168         return nuget_restoration;
169 }
170
171 static void copyLibraryCreateSymlink(const std::string pkgId, std::vector<std::string> LibrariesInfo)
172 {
173         if (LibrariesInfo.empty()) {
174                 return;
175         }
176
177         for (auto& librarySha : LibrariesInfo) {
178                 std::string library = librarySha.substr(0, librarySha.find(':'));
179                 std::string filename = library.substr(library.rfind('/') + 1);
180                 std::string fileSha = filename + ".." + librarySha.substr(librarySha.find(':') + 1);
181                 std::string shaPath = concatPath(TLC_LIBRARIES_DIR, fileSha);
182                 bool fileCopied = false;
183                 if (!exist(shaPath)) {
184                         if (!copyFile(library, shaPath)) {
185                                 _ERR("Failed to copy of %s", filename.c_str());
186                                 continue;
187                         }
188                         fileCopied = true;
189                         createLibraries.push_back(shaPath);
190                 }
191                 if (!removeFile(library)) {
192                         _ERR("Failed to remove of %s", library.c_str());
193                         if (fileCopied) {
194                                 removeFile(shaPath);
195                         }
196                         continue;
197                 }
198                 bs::error_code error;
199                 bf::create_symlink(shaPath, library, error);
200                 if (error) {
201                         _ERR("Failed to create symlink %s file", library.c_str());
202                         copyFile(shaPath, library);
203                         copySmackAndOwnership(getBaseName(library), library);
204                         if (fileCopied) {
205                                 removeFile(shaPath);
206                         }
207                         continue;
208                 }
209                 copySmackAndOwnership(getBaseName(library), library, true);
210
211                 if (tlc_insertDB(pkgId, fileSha) != 0) {
212                         copyFile(shaPath, library);
213                         copySmackAndOwnership(getBaseName(library), library);
214                         if (fileCopied) {
215                                 removeFile(shaPath);
216                         }
217                         continue;
218                 }
219         }
220 }
221
222 static void checkDepsJson(std::string rootPath, std::string binPath, std::string execName)
223 {
224         for (auto& npAssembly : depsJsonParser(rootPath, execName)) {
225                 std::string nuget_package = npAssembly.substr(0, npAssembly.rfind(':'));
226                 std::string assembly_name = npAssembly.substr(npAssembly.rfind(':') + 1);
227                 tacDB.push_back(nuget_package);
228                 std::string buffer = SHA256(concatPath(binPath, assembly_name));
229                 nugetPackagesAssembliesSha.push_back(nuget_package + ":" + assembly_name + ":" + buffer);
230                 _INFO("Assembly : [%s] / SHA256 : [%s]", assembly_name.c_str(), buffer.c_str());
231         }
232         std::sort(tacDB.begin(), tacDB.end());
233         tacDB.erase(unique(tacDB.begin(), tacDB.end()), tacDB.end());
234 }
235
236 static int generateTAC(const std::string& pkgId, const std::string& binPath)
237 {
238         std::string tac_dir = concatPath(binPath, TAC_SYMLINK_SUB_DIR);
239         if (!createDir(tac_dir)) {
240                 _ERR("Cannot create directory: %s", tac_dir.c_str());
241                 return 0;
242         }
243
244         copySmackAndOwnership(binPath.c_str(), tac_dir.c_str());
245
246         for (auto& np : tacDB) {
247                 std::string tac_name = np.substr(0, np.find('/'));
248                 std::string tac_version = np.substr(np.rfind('/') + 1);
249                 _INFO("TAC name : %s", tac_name.c_str());
250                 _INFO("TAC version : %s", tac_version.c_str());
251
252                 bs::error_code error;
253                 std::string tac_version_dir = concatPath(__DOTNET_DIR, np);
254                 std::string sha256_info = concatPath(tac_version_dir, TAC_SHA_256_INFO);
255                 bool isCreateTacDir = false;
256                 if (!exist(tac_version_dir)) {
257                         _INFO("Create tac_version_dir [%s]", tac_version_dir.c_str());
258
259                         if (!createDir(tac_version_dir)) {
260                                 _ERR("Cannot create directory: %s", tac_version_dir.c_str());
261                                 tacState = TAC_STATE_RESTORE;
262                                 return -1;
263                         }
264                         createDirectories.push_back(tac_version_dir);
265
266                         if (isSymlinkFile(sha256_info)) {
267                                 _ERR("Failed to create sha256_info. Symbolic link is detected");
268                                 tacState = TAC_STATE_RESTORE;
269                                 return -1;
270                         }
271
272                         createSHA256Info(sha256_info, np);
273
274                         if (!exist(sha256_info)) {
275                                 tacState = TAC_STATE_RESTORE;
276                                 return -1;
277                         }
278
279                         isCreateTacDir = true;
280                 } else {
281                         _INFO("Exists tac_version_dir [%s]", tac_version_dir.c_str());
282
283                         if (isSymlinkFile(sha256_info)) {
284                                 _ERR("Failed to create sha256_info. Symbolic link is detected");
285                                 tacState = TAC_STATE_RESTORE;
286                                 return -1;
287                         }
288
289                         if (!compareSHA256Info(sha256_info, np)) {
290                                 _INFO("Different nuget : %s", np.c_str());
291                                 continue;
292                         }
293
294                         isCreateTacDir = false;
295                 }
296
297                 if (copyAssemblyCreateSymlink(binPath, tac_dir, np, isCreateTacDir)) {
298                         _ERR("Failed to create symlink");
299                         tacState = TAC_STATE_RESTORE;
300                         return -1;
301                 }
302
303                 if (tacState == TAC_STATE_INSTALL) {
304                         if (tac_insertDB(pkgId, np, tac_name, tac_version) != 0) {
305                                 tacState = TAC_STATE_RESTORE;
306                                 return -1;
307                         }
308                 } else if (tacState == TAC_STATE_UPGRADE) {
309                         int count = -1;
310                         if (tac_countDB(pkgId, tac_name, "", count) != 0) {
311                                 tacState = TAC_STATE_RESTORE;
312                                 return -1;
313                         }
314                         if (count == 1) {
315                                 if (tac_updateDB(pkgId, np, tac_name, tac_version) != 0) {
316                                         tacState = TAC_STATE_RESTORE;
317                                         return -1;
318                                 }
319                         } else if (count == 0) {
320                                 if (tac_insertDB(pkgId, np, tac_name, tac_version) != 0) {
321                                         tacState = TAC_STATE_RESTORE;
322                                         return -1;
323                                 }
324                         }
325                 }
326         }
327         return 0;
328 }
329
330 void tacUpdateDB(const std::string& pkgId)
331 {
332         for (auto& unp : updateTac) {
333                 int count = -1;
334                 if (tac_countDB(pkgId, "", unp, count) != 0) {
335                         continue;
336                 }
337
338                 if (count == 0) {
339                         std::string tac_version_dir_prev = concatPath(__DOTNET_DIR, unp);
340                         std::string tac_version_dir_backup = tac_version_dir_prev + ".bck";
341                         if (!copyDir(tac_version_dir_prev, tac_version_dir_backup)) {
342                                 _ERR("Failed to copy of %s to %s", tac_version_dir_prev.c_str(), tac_version_dir_backup.c_str());
343                                 continue;
344                         }
345                         if (!removeAll(tac_version_dir_prev)) {
346                                 _ERR("Failed to remove of %s", tac_version_dir_prev.c_str());
347                                 continue;
348                         }
349                 }
350         }
351 }
352
353 void tlcUpdateDB(const std::string& pkgId)
354 {
355         for (auto& ulp : updateTlc) {
356                 int count = -1;
357                 if (tlc_countDB(pkgId, ulp, count) != 0) {
358                         continue;
359                 }
360
361                 if (count == 0) {
362                         std::string library_prev = concatPath(TLC_LIBRARIES_DIR, ulp);
363                         std::string library_backup = library_prev + ".bck";
364                         if (!copyFile(library_prev, library_backup)) {
365                                 _ERR("Failed to copy of %s", library_prev.c_str());
366                                 continue;
367                         }
368                         if (!removeFile(library_prev)) {
369                                 _ERR("Failed to remove of %s", library_prev.c_str());
370                                 continue;
371                         }
372                 }
373         }
374 }
375
376 int tacInstall(const std::string& pkgId, tac_state state, bool tacForce)
377 {
378         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_INSTALL =====]");
379         _INFO("PackageID : %s", pkgId.c_str());
380
381         // Can be multiple apps in one package
382         if (tacPluginInstalled) {
383                 _INFO("TAC plugin already installed");
384                 return 0;
385         }
386         tacPluginInstalled = true;
387
388         std::string appType = getAppType(pkgId);
389         if (strstr(appType.c_str(), "dotnet") == NULL) {
390                 _ERR("App type is not dotnet");
391                 return 0;
392         }
393         std::string execName = getExecName(pkgId);
394         std::string rootPath = getRootPath(pkgId);
395         if (execName.empty() || rootPath.empty()) {
396                 return 0;
397         }
398
399         std::string binPath = concatPath(rootPath, "bin");
400         if (exist(concatPath(binPath, PRE_COMPILED_PACKAGE_FILE))) {
401                 _INFO("The %s is a Pre-Compiled package. So, skip the TAC", pkgId.c_str());
402                 return 0;
403         }
404
405         std::string metaValue = getMetadataValue(pkgId, TAC_METADATA_KEY);
406         if (!tacForce) {
407                 if (metaValue.empty()) {
408                         return 0;
409                 }
410         }
411         if (metaValue == METADATA_VALUE_TRUE || tacForce) {
412                 checkDepsJson(rootPath, binPath, execName);
413         }
414
415         tacState = state;
416         if (tacDB.empty()) {
417                 return 0;
418         }
419
420         if (tac_createDB() != 0) {
421                 return -1;
422         }
423
424         if (generateTAC(pkgId, binPath) != 0) {
425                 return -1;
426         }
427
428         ///// TLC /////
429         if (tlc_createDB() != 0) {
430                 return -1;
431         }
432
433         copyLibraryCreateSymlink(pkgId, getLibrariesInfo(rootPath));
434
435         return 0;
436 }
437
438 int tacUpgrade(const std::string& pkgId, tac_state state, bool tacForce)
439 {
440         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_UPGRADE =====]");
441         _INFO("PackageID : %s", pkgId.c_str());
442
443         // Can be multiple apps in one package
444         if (tacPluginInstalled) {
445                 _INFO("TAC plugin already upgraded");
446                 return 0;
447         }
448         tacPluginInstalled = true;
449
450         std::string appType = getAppType(pkgId);
451         if (strstr(appType.c_str(), "dotnet") == NULL) {
452                 _ERR("App type is not dotnet");
453                 return 0;
454         }
455         std::string execName = getExecName(pkgId);
456         std::string rootPath = getRootPath(pkgId);
457         if (execName.empty() || rootPath.empty()) {
458                 return 0;
459         }
460
461         std::string binPath = concatPath(rootPath, "bin");
462         if (exist(concatPath(binPath, PRE_COMPILED_PACKAGE_FILE))) {
463                 _INFO("The %s is a Pre-Compiled package. So, skip the TAC", pkgId.c_str());
464                 state = TAC_STATE_REMOVED;
465         }
466
467         if (state == TAC_STATE_REMOVED) {
468                 _INFO("Skipped to parse of deps.json");
469         } else { //TAC_STATE_UPGRADE
470                 std::string metaValue = getMetadataValue(pkgId, TAC_METADATA_KEY);
471                 if (!tacForce) {
472                         if (metaValue.empty()) {
473                                 return 0;
474                         }
475                 }
476                 if (metaValue == METADATA_VALUE_TRUE || tacForce) {
477                         checkDepsJson(rootPath, binPath, execName);
478                 }
479         }
480
481         tacState = TAC_STATE_UPGRADE;
482         if (tac_createDB() != 0) {
483                 return -1;
484         }
485
486         updateTac = tac_selectDB(pkgId);
487
488         bool skipTLC = false;
489         if (tacDB.empty()) {
490                 if (tac_deleteDB(pkgId, "") != 0) {
491                         return -1;
492                 }
493                 tacUpdateDB(pkgId);
494
495                 skipTLC = true;
496         } else {
497                 if (generateTAC(pkgId, binPath) != 0) {
498                         return -1;
499                 }
500
501                 for (auto& unp : updateTac) {
502                         bool isExits = false;
503                         for (auto& np : tacDB) {
504                                 if (!strcmp(unp.c_str(), np.c_str())) {
505                                         isExits = true;
506                                         break;
507                                 }
508                         }
509                         if (!isExits) {
510                                 if (tac_deleteDB(pkgId, unp) != 0) {
511                                         tacState = TAC_STATE_RESTORE;
512                                         return -1;
513                                 }
514                         }
515                 }
516                 tacUpdateDB(pkgId);
517         }
518
519         ///// TLC /////
520         if (tlc_createDB() != 0) {
521                 return -1;
522         }
523
524         updateTlc = tlc_selectDB(pkgId);
525
526         if (tlc_deleteDB(pkgId) != 0) {
527                 return -1;
528         }
529
530         tlcUpdateDB(pkgId);
531
532         copyLibraryCreateSymlink(pkgId, skipTLC ? std::vector<std::string>() : getLibrariesInfo(rootPath));
533
534         return 0;
535 }
536
537 int tacUninstall(const std::string& pkgId, tac_state state)
538 {
539         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_UNINSTALL =====]");
540         _INFO("PackageID : %s", pkgId.c_str());
541
542         // Can be multiple apps in one package
543         if (tacPluginInstalled) {
544                 _INFO("TAC plugin already uninstalled");
545                 return 0;
546         }
547         tacPluginInstalled = true;
548
549         tacState= state;
550         if (tac_openDB() != 0) {
551                 return -1;
552         }
553
554         updateTac = tac_selectDB(pkgId);
555
556         if (tac_deleteDB(pkgId, "") != 0) {
557                 tacState = TAC_STATE_RESTORE;
558                 return -1;
559         }
560         tacUpdateDB(pkgId);
561
562         ///// TLC /////
563         if (tlc_openDB() != 0) {
564                 return -1;
565         }
566
567         updateTlc = tlc_selectDB(pkgId);
568
569         if (tlc_deleteDB(pkgId) != 0) {
570                 return -1;
571         }
572
573         tlcUpdateDB(pkgId);
574
575         return 0;
576 }
577
578 int tacRemoved(const std::string& pkgId)
579 {
580         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_REMOVED =====]");
581         _INFO("PackageID : %s", pkgId.c_str());
582
583         return tacUpgrade(pkgId, TAC_STATE_REMOVED);
584 }
585
586 void undoStep(std::string tac)
587 {
588         std::string current_tac = concatPath(__DOTNET_DIR, tac.substr(0, tac.find('/')));
589         try {
590                 for (auto& bck : bf::recursive_directory_iterator(current_tac)) {
591                         std::string bck_path = bck.path().string();
592                         if (bf::is_directory(bck_path) && strstr(bck_path.c_str(), ".bck") != NULL) {
593                                 if (!moveFile(bck_path, bck_path.substr(0, bck_path.rfind(".bck")))) {
594                                         _ERR("Failed to move %s", bck_path.c_str());
595                                 }
596                                 break;
597                         }
598                 }
599         } catch (const bf::filesystem_error& error) {
600                 _ERR("Failed to recursive directory: %s", error.what());
601                 return;
602         }
603
604         ///// TLC /////
605         auto convert = [](const std::string& path, const std::string& filename) {
606                 if (filename.rfind(".bck") != std::string::npos) {
607                         if (!moveFile(path, replaceAll(path, ".bck", ""))) {
608                                 _ERR("Failed to move %s", path.c_str());
609                         }
610                 }
611         };
612
613         scanFilesInDirectory(TLC_LIBRARIES_DIR, convert, 0);
614 }
615
616 void install_Undo()
617 {
618         for (auto& cd : createDirectories) {
619                 if (!removeAll(cd)) {
620                         _ERR("Failed to remove of %s", cd.c_str());
621                 }
622         }
623
624         for (auto& cl : createLibraries) {
625                 if (!removeFile(cl)) {
626                         _ERR("Failed to remove of %s", cl.c_str());
627                 }
628         }
629 }
630
631 void unInstall_Undo()
632 {
633         for (auto& unp : updateTac) {
634                 undoStep(unp);
635         }
636 }
637
638 void update_Undo()
639 {
640         install_Undo();
641         if (!tacDB.empty()) {
642                 for (auto& np : tacDB) {
643                         undoStep(np);
644                 }
645         }
646         unInstall_Undo();
647 }
648
649 int tacUndo(const std::string& pkgId)
650 {
651         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_UNDO =====]");
652         _INFO("PackageID : %s", pkgId.c_str());
653
654         // Can be multiple apps in one package
655         if (tacPluginFinished) {
656                 _INFO("TAC plugin already finished(UNDO)");
657                 return 0;
658         }
659         tacPluginFinished = true;
660
661         if (tacState == TAC_STATE_INSTALL) {
662                 install_Undo();
663         } else if (tacState == TAC_STATE_UPGRADE) {
664                 update_Undo();
665         } else if (tacState == TAC_STATE_UNINSTALL) {
666                 unInstall_Undo();
667         } else if (tacState == TAC_STATE_RESTORE) {
668                 update_Undo();
669         }
670
671         tac_rollbackDB();
672
673         tlc_rollbackDB();
674
675         return 0;
676 }
677
678 void changeOwnershipTAC(std::string current_tac)
679 {
680         copySmackAndOwnership(__DOTNET_DIR, current_tac);
681         try {
682                 for (auto& path : bf::recursive_directory_iterator(current_tac))
683                         copySmackAndOwnership(__DOTNET_DIR, path.path().string());
684         } catch (const bf::filesystem_error& error) {
685                 _ERR("Failed to recursive directory: %s", error.what());
686         }
687 }
688
689 void cleanStep(std::string tac)
690 {
691         std::string current_tac = concatPath(__DOTNET_DIR, tac.substr(0, tac.find('/')));
692         try {
693                 for (auto& bck : bf::recursive_directory_iterator(current_tac)) {
694                         std::string bck_path = bck.path().string();
695                         if (bf::is_directory(bck_path) && strstr(bck_path.c_str(), ".bck") != NULL) {
696                                 if (!removeAll(bck_path)) {
697                                         _ERR("Failed to remove of %s", bck_path.c_str());
698                                 }
699                                 break;
700                         }
701                 }
702
703                 bool isExist = false;
704                 for (auto& bck : bf::recursive_directory_iterator(current_tac)) {
705                         std::string bck_path = bck.path().string();
706                         if (exist(bck_path) && bf::is_directory(bck_path) && strstr(bck_path.c_str(), ".bck") == NULL) {
707                                 isExist = true;
708                                 break;
709                         }
710                 }
711                 if (!isExist) {
712                         if (!removeAll(current_tac)) {
713                                 _ERR("Failed to remove of %s", current_tac.c_str());
714                         }
715                 }
716         } catch (const bf::filesystem_error& error) {
717                 _ERR("Failed to recursive directory: %s", error.what());
718                 return;
719         }
720
721         ///// TLC /////
722         auto convert = [](const std::string& path, const std::string& filename) {
723                 if (filename.rfind(".bck") != std::string::npos) {
724                         if (!removeFile(path)) {
725                                 _ERR("Failed to remove of %s", path.c_str());
726                         }
727                 }
728         };
729
730         scanFilesInDirectory(TLC_LIBRARIES_DIR, convert, 0);
731 }
732
733 void install_Clean()
734 {
735         for (auto& cd : createDirectories) {
736                 changeOwnershipTAC(cd);
737                 copySmackAndOwnership(__DOTNET_DIR, cd.substr(0, cd.rfind('/')));
738         }
739
740         for (auto& cl : createLibraries) {
741                 copySmackAndOwnership(__DOTNET_DIR, cl);
742         }
743 }
744
745 void unInstall_Clean()
746 {
747         for (auto& unp : updateTac) {
748                 cleanStep(unp);
749         }
750 }
751
752 void update_Clean()
753 {
754         install_Clean();
755         if (!tacDB.empty()) {
756                 for (auto& np : tacDB) {
757                         cleanStep(np);
758                         changeOwnershipTAC(concatPath(__DOTNET_DIR, np.substr(0, np.find('/'))));
759                 }
760         }
761         unInstall_Clean();
762 }
763
764 int tacClean(const std::string& pkgId)
765 {
766         if (tacState == TAC_STATE_RESTORE) {
767                 disableTACPackage(pkgId);
768
769                 std::string rootPath = getRootPath(pkgId);
770                 if (!rootPath.empty()) {
771                         std::string binPath = concatPath(rootPath, "bin");
772                         removeAll(concatPath(binPath, TAC_SYMLINK_SUB_DIR));
773                 }
774
775                 std::string runtimesDir = concatPath(rootPath, "bin/runtimes");
776                 if (exist(runtimesDir)) {
777                         char buffer[128];
778                         sprintf(buffer, "(tizen|linux|unix|base|any)(.\\d.\\d.\\d)?(-%s)?", ARCHITECTURE_IDENTIFIER);
779                         std::regex pattern(buffer);
780
781                         try {
782                                 for (auto& path : bf::recursive_directory_iterator(runtimesDir)) {
783                                         std::string symPath = path.path().string();
784                                         if (isDirectory(symPath) || !isSymlinkFile(symPath))
785                                                 continue;
786                                         std::string targetDir = symPath.substr(symPath.rfind("/runtimes/") + 10);
787                                         if (!std::regex_match(targetDir.substr(0, targetDir.find('/')), pattern))
788                                                 continue;
789                                         if (symPath.rfind(".so") == std::string::npos)
790                                                 continue;
791                                         copyFile(bf::read_symlink(symPath).string(), symPath);
792                                 }
793                         } catch (const bf::filesystem_error& error) {
794                                 _ERR("Failed to recursive directory: %s", error.what());
795                         }
796                 }
797
798                 return tacUndo(pkgId);
799         }
800
801         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_CLEAN =====]");
802         _INFO("PackageID : %s", pkgId.c_str());
803
804         // Can be multiple apps in one package
805         if (tacPluginFinished) {
806                 _INFO("TAC plugin already finished(CLEAN)");
807                 return 0;
808         }
809         tacPluginFinished = true;
810
811         if (tacState == TAC_STATE_INSTALL) {
812                 install_Clean();
813         } else if (tacState == TAC_STATE_UPGRADE) {
814                 update_Clean();
815         } else if (tacState == TAC_STATE_UNINSTALL) {
816                 unInstall_Clean();
817         }
818
819         if (tac_closeDB()) {
820                 copySmackAndOwnership(__DOTNET_DIR, TAC_APP_LIST_DB);
821                 copySmackAndOwnership(__DOTNET_DIR, TAC_APP_LIST_DB + std::string("-journal"));
822         }
823
824         if (tlc_closeDB()) {
825                 copySmackAndOwnership(__DOTNET_DIR, TLC_APP_LIST_DB);
826                 copySmackAndOwnership(__DOTNET_DIR, TLC_APP_LIST_DB + std::string("-journal"));
827         }
828
829         return 0;
830 }
831