Fix writing recovery file
[platform/core/appfw/app-installers.git] / src / common / recovery_file.cc
1 // Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by an apache-2.0 license that can be
3 // found in the LICENSE file.
4
5 #include "common/recovery_file.h"
6
7 #include <boost/filesystem/operations.hpp>
8 #include <boost/iostreams/stream.hpp>
9 #include <boost/iostreams/device/file_descriptor.hpp>
10 #include <boost/system/error_code.hpp>
11
12 #include <manifest_parser/utils/logging.h>
13
14 #include <array>
15 #include <cstring>
16 #include <string>
17 #include <map>
18 #include <utility>
19
20 #include "common/installer_context.h"
21 #include "common/utils/file_util.h"
22
23 namespace bf = boost::filesystem;
24 namespace bs = boost::system;
25 namespace bi = boost::iostreams;
26 namespace ci = common_installer;
27
28 namespace {
29
30 const char kRecoveryInstallString[] = "NEW";
31 const char kRecoveryUpdateString[] = "UPDATE";
32 const char kRecoveryUninstallationString[] = "UNINSTALL";
33 const char kRecoveryRdsString[] = "RDS";
34 const char kRecoveryDeltaString[] = "DELTA";
35 const char kRecoveryMountInstallString[] = "MOUNTINSTALL";
36 const char kRecoveryMountUpdateString[] = "MOUNTUPDATE";
37 const char kRecoveryReadonlyUpdateInstallString[] = "READONLYUPDATEINSTALL";
38 const char kRecoveryUnknownString[] = "UNKNOWN";
39
40 const std::map<std::string, ci::RequestType> kStringToRequestMap = {
41   {kRecoveryInstallString, ci::RequestType::Install},
42   {kRecoveryUpdateString, ci::RequestType::Update},
43   {kRecoveryUninstallationString, ci::RequestType::Uninstall},
44   {kRecoveryRdsString, ci::RequestType::Reinstall},
45   {kRecoveryDeltaString, ci::RequestType::Delta},
46   {kRecoveryMountInstallString, ci::RequestType::MountInstall},
47   {kRecoveryMountUpdateString, ci::RequestType::MountUpdate},
48   {kRecoveryReadonlyUpdateInstallString,
49       ci::RequestType::ReadonlyUpdateInstall},
50 };
51
52 std::string TruncateNewLine(const char* data) {
53   int length = strlen(data);
54   if (data[length - 1] == '\n')
55       --length;
56   return std::string(data, length);
57 }
58
59 }  // namespace
60
61 namespace common_installer {
62 namespace recovery {
63
64 std::unique_ptr<RecoveryFile> RecoveryFile::CreateRecoveryFile(
65     const boost::filesystem::path& path, RequestType type) {
66   if (bf::exists(path)) {
67     LOG(ERROR) << "Recovery file already exists!";
68     return nullptr;
69   }
70   std::unique_ptr<RecoveryFile> file(new RecoveryFile(path, type, false));
71   if (file->is_detached()) {
72     LOG(ERROR) << "Failed to access file";
73     return nullptr;
74   }
75   return file;
76 }
77
78 std::unique_ptr<RecoveryFile> RecoveryFile::OpenRecoveryFile(
79     const boost::filesystem::path& path) {
80   if (!bf::exists(path)) {
81     LOG(ERROR) << "Cannot open recovery file";
82     return nullptr;
83   }
84   std::unique_ptr<RecoveryFile> file(new RecoveryFile(path,
85       RequestType::Unknown, true));
86   if (file->is_detached()) {
87     LOG(ERROR) << "Failed to read recovery file";
88     return nullptr;
89   }
90   return file;
91 }
92
93 RecoveryFile::RecoveryFile(const bf::path& path, RequestType type, bool load)
94     : type_(type), path_(path), backup_done_(false), cleanup_(false) {
95   backup_path_ = path_.string() + ".bck";
96   if (load) {
97     if (!ReadFileContent()) {
98       path_.clear();
99       return;
100     }
101   } else {
102     // create file
103     if (!WriteAndCommitFileContent()) {
104       path_.clear();
105       return;
106     }
107     LOG(DEBUG) << "Recovery file " << path_ << " created";
108   }
109 }
110
111 RecoveryFile::~RecoveryFile() {
112   if (Remove(path_))
113     LOG(DEBUG) << "Recovery file " << path_ << " removed";
114   if (Remove(backup_path_))
115     LOG(DEBUG) << "Recovery file " << backup_path_ << " removed";
116 }
117
118 void RecoveryFile::Detach() {
119   path_.clear();
120 }
121
122 bool RecoveryFile::is_detached() const {
123   return path_.empty();
124 }
125
126 void RecoveryFile::set_unpacked_dir(
127     boost::filesystem::path unpacked_dir) {
128   unpacked_dir_ = std::move(unpacked_dir);
129 }
130
131 void RecoveryFile::set_pkgid(std::string pkgid) {
132   pkgid_ = std::move(pkgid);
133 }
134
135
136 void RecoveryFile::set_backup_done(bool backup_done) {
137   backup_done_ = backup_done;
138 }
139
140 void RecoveryFile::set_cleanup(bool cleanup) {
141   cleanup_ = cleanup;
142 }
143
144 const boost::filesystem::path& RecoveryFile::unpacked_dir() const {
145   return unpacked_dir_;
146 }
147
148 const std::string& RecoveryFile::pkgid() const {
149   return pkgid_;
150 }
151
152 RequestType RecoveryFile::type() const {
153   return type_;
154 }
155
156 bool RecoveryFile::backup_done() const {
157   return backup_done_;
158 }
159
160 bool RecoveryFile::cleanup() const {
161   return cleanup_;
162 }
163
164 bool RecoveryFile::ReadFileContent() {
165   FILE* handle = fopen(path_.c_str(), "r");
166   if (!handle) {
167     LOG(ERROR) << "Cannot read recovery file";
168     return false;
169   }
170   std::array<char, 200> data;
171   data[0] = '\0';
172   if (!fgets(data.data(), data.size(), handle)) {
173     type_ = RequestType::Unknown;
174     fclose(handle);
175     return true;
176   }
177   std::string mode(TruncateNewLine(data.data()));
178   auto iter = kStringToRequestMap.find(mode);
179   if (iter == kStringToRequestMap.end()) {
180     type_ = RequestType::Unknown;
181   } else {
182     type_ = iter->second;
183   }
184
185   if (!fgets(data.data(), data.size(), handle)) {
186     fclose(handle);
187     return true;
188   }
189   unpacked_dir_ = TruncateNewLine(data.data());
190   if (!fgets(data.data(), data.size(), handle)) {
191     fclose(handle);
192     return true;
193   }
194   pkgid_ = TruncateNewLine(data.data());
195   if (!fgets(data.data(), data.size(), handle)) {
196     fclose(handle);
197     return true;
198   }
199   std::string backup_flag = TruncateNewLine(data.data());
200   if (backup_flag == "true")
201     backup_done_ = true;
202   else
203     backup_done_ = false;
204   if (!fgets(data.data(), data.size(), handle)) {
205     fclose(handle);
206     return true;
207   }
208   std::string cleanup_flag = TruncateNewLine(data.data());
209   if (cleanup_flag == "cleanup")
210     cleanup_ = true;
211   else
212     cleanup_ = false;
213   fclose(handle);
214   return true;
215 }
216
217 bool RecoveryFile::WriteAndCommitFileContent() {
218   if (bf::exists(path_))  {
219     bs::error_code error;
220     bf::rename(path_, backup_path_, error);
221     if (error) {
222       LOG(ERROR) << "Cannot backup recovery file:" << path_ <<
223           ", error: " << error;
224       return false;
225     }
226   }
227
228   bi::stream<bi::file_descriptor_sink> ofs(path_);
229   if (!ofs) {
230     LOG(ERROR) << "Cannot write recovery file";
231     return false;
232   }
233
234   switch (type_) {
235   case RequestType::Install:
236     ofs << kRecoveryInstallString << std::endl;
237     break;
238   case RequestType::Update:
239     ofs << kRecoveryUpdateString << std::endl;
240     break;
241   case RequestType::Uninstall:
242     ofs << kRecoveryUninstallationString << std::endl;
243     break;
244   case RequestType::Reinstall:
245     ofs << kRecoveryRdsString << std::endl;
246     break;
247   case RequestType::Delta:
248     ofs << kRecoveryDeltaString << std::endl;
249     break;
250   case RequestType::MountInstall:
251     ofs << kRecoveryMountInstallString << std::endl;
252     break;
253   case RequestType::MountUpdate:
254     ofs << kRecoveryMountUpdateString << std::endl;
255     break;
256   case RequestType::ReadonlyUpdateInstall:
257     ofs << kRecoveryReadonlyUpdateInstallString << std::endl;
258     break;
259   default:
260     ofs << kRecoveryUnknownString << std::endl;
261     break;
262   }
263   ofs << unpacked_dir_.c_str() << std::endl;
264   ofs << pkgid_ << std::endl;
265   ofs << (backup_done_ ? "true" : "false") << std::endl;
266   ofs << (cleanup_ ? "cleanup" : "rollback") << std::endl;
267   ofs.flush();
268   ::fsync(ofs->handle());
269   ofs.close();
270
271   Remove(backup_path_);
272   return true;
273 }
274
275 }  // namespace recovery
276 }  // namespace common_installer
277