1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <cerrno>
17 #include <condition_variable>
18 #include <cstdint>
19 #include <cstring>
20 #include <fcntl.h>
21 #include <memory>
22 #include <mutex>
23 #include <regex>
24 #include <set>
25 #include <string>
26 #include <sys/sendfile.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <tuple>
30 #include <unistd.h>
31 #include <vector>
32
33 #include "b_error/b_error.h"
34 #include "b_filesystem/b_dir.h"
35 #include "b_filesystem/b_file.h"
36 #include "b_json/b_json_entity_caps.h"
37 #include "b_json/b_json_entity_ext_manage.h"
38 #include "b_resources/b_constants.h"
39 #include "backup_kit_inner.h"
40 #include "base/hiviewdfx/hitrace/interfaces/native/innerkits/include/hitrace_meter/hitrace_meter.h"
41 #include "errors.h"
42 #include "service_proxy.h"
43 #include "tools_op.h"
44
45 namespace OHOS::FileManagement::Backup {
46 using namespace std;
47
48 class Session {
49 public:
UpdateBundleSendFiles(const BundleName & bundleName,const string & fileName)50 void UpdateBundleSendFiles(const BundleName &bundleName, const string &fileName)
51 {
52 lock_guard<mutex> lk(lock_);
53 bundleStatusMap_[bundleName].sendFile.insert(fileName);
54 }
55
UpdateBundleSentFiles(const BundleName & bundleName,const string & fileName)56 void UpdateBundleSentFiles(const BundleName &bundleName, const string &fileName)
57 {
58 lock_guard<mutex> lk(lock_);
59 bundleStatusMap_[bundleName].sentFile.insert(fileName);
60 TryClearBundleOfMap(bundleName);
61 }
62
ClearBundleOfMap(const BundleName & bundleName)63 void ClearBundleOfMap(const BundleName &bundleName)
64 {
65 lock_guard<mutex> lk(lock_);
66 bundleStatusMap_.erase(bundleName);
67 }
68
TryNotify(bool flag=false)69 void TryNotify(bool flag = false)
70 {
71 if (flag == true) {
72 ready_ = true;
73 cv_.notify_all();
74 } else if (bundleStatusMap_.size() == 0 && cnt_ == 0 && isAllBundelsFinished.load()) {
75 ready_ = true;
76 cv_.notify_all();
77 }
78 }
79
UpdateBundleFinishedCount()80 void UpdateBundleFinishedCount()
81 {
82 lock_guard<mutex> lk(lock_);
83 cnt_--;
84 }
85
SetBundleFinishedCount(uint32_t cnt)86 void SetBundleFinishedCount(uint32_t cnt)
87 {
88 cnt_ = cnt;
89 }
90
Wait()91 void Wait()
92 {
93 unique_lock<mutex> lk(lock_);
94 cv_.wait(lk, [&] { return ready_; });
95 }
96
97 unique_ptr<BSessionRestore> session_ = {};
98
99 private:
100 struct BundleStatus {
101 set<string> sendFile;
102 set<string> sentFile;
103 };
104
TryClearBundleOfMap(const BundleName & bundleName)105 void TryClearBundleOfMap(const BundleName &bundleName)
106 {
107 if (bundleStatusMap_[bundleName].sendFile == bundleStatusMap_[bundleName].sentFile) {
108 bundleStatusMap_.erase(bundleName);
109 }
110 }
111
112 map<string, BundleStatus> bundleStatusMap_;
113 mutable condition_variable cv_;
114 mutex lock_;
115 bool ready_ = false;
116 uint32_t cnt_ {0};
117
118 public:
119 std::atomic<bool> isAllBundelsFinished {false};
120 };
121
GenHelpMsg()122 static string GenHelpMsg()
123 {
124 return "\t\tThis operation helps to restore application data.\n"
125 "\t\t--pathCapFile\t\t This parameter should be the path of the capability file.\n"
126 "\t\t--bundle\t\t This parameter is bundleName.";
127 }
128
OnFileReady(shared_ptr<Session> ctx,const BFileInfo & fileInfo,UniqueFd fd)129 static void OnFileReady(shared_ptr<Session> ctx, const BFileInfo &fileInfo, UniqueFd fd)
130 {
131 printf("FileReady owner = %s, fileName = %s, sn = %u, fd = %d\n", fileInfo.owner.c_str(), fileInfo.fileName.c_str(),
132 fileInfo.sn, fd.Get());
133 if (!regex_match(fileInfo.fileName, regex("^[0-9a-zA-Z_.]+$")) &&
134 fileInfo.fileName != BConstants::RESTORE_INSTALL_PATH) {
135 throw BError(BError::Codes::TOOL_INVAL_ARG, "Filename is not alphanumeric");
136 }
137 string tmpPath;
138 if (fileInfo.fileName == BConstants::RESTORE_INSTALL_PATH) {
139 printf("OnFileReady bundle hap\n");
140 tmpPath = string(BConstants::BACKUP_TOOL_INSTALL_DIR) + fileInfo.owner + ".hap";
141 } else {
142 tmpPath = string(BConstants::BACKUP_TOOL_RECEIVE_DIR) + fileInfo.owner + "/" + fileInfo.fileName;
143 }
144 if (access(tmpPath.data(), F_OK) != 0) {
145 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
146 }
147 UniqueFd fdLocal(open(tmpPath.data(), O_RDONLY));
148 if (fdLocal < 0) {
149 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
150 }
151 BFile::SendFile(fd, fdLocal);
152 int ret = ctx->session_->PublishFile(fileInfo);
153 if (ret != 0) {
154 throw BError(BError::Codes::TOOL_INVAL_ARG, "PublishFile error");
155 }
156 if (fileInfo.fileName != BConstants::RESTORE_INSTALL_PATH) {
157 ctx->UpdateBundleSentFiles(fileInfo.owner, fileInfo.fileName);
158 }
159 ctx->TryNotify();
160 }
161
OnBundleStarted(shared_ptr<Session> ctx,ErrCode err,const BundleName name)162 static void OnBundleStarted(shared_ptr<Session> ctx, ErrCode err, const BundleName name)
163 {
164 printf("BundleStarted errCode = %d, BundleName = %s\n", err, name.c_str());
165 if (err != 0) {
166 ctx->UpdateBundleFinishedCount();
167 ctx->isAllBundelsFinished.store(true);
168 ctx->ClearBundleOfMap(name);
169 ctx->TryNotify();
170 }
171 }
172
OnBundleFinished(shared_ptr<Session> ctx,ErrCode err,const BundleName name)173 static void OnBundleFinished(shared_ptr<Session> ctx, ErrCode err, const BundleName name)
174 {
175 printf("BundleFinished errCode = %d, BundleName = %s\n", err, name.c_str());
176 ctx->UpdateBundleFinishedCount();
177 if (err != 0) {
178 ctx->isAllBundelsFinished.store(true);
179 ctx->ClearBundleOfMap(name);
180 }
181 ctx->TryNotify();
182 }
183
OnAllBundlesFinished(shared_ptr<Session> ctx,ErrCode err)184 static void OnAllBundlesFinished(shared_ptr<Session> ctx, ErrCode err)
185 {
186 ctx->isAllBundelsFinished.store(true);
187 if (err == 0) {
188 printf("Restore successful\n");
189 } else {
190 printf("Failed to Unplanned Abort error: %d\n", err);
191 ctx->TryNotify(true);
192 return;
193 }
194 ctx->TryNotify();
195 }
196
OnBackupServiceDied(shared_ptr<Session> ctx)197 static void OnBackupServiceDied(shared_ptr<Session> ctx)
198 {
199 printf("backupServiceDied\n");
200 ctx->TryNotify(true);
201 }
202
RestoreApp(shared_ptr<Session> restore,vector<BundleName> & bundleNames)203 static void RestoreApp(shared_ptr<Session> restore, vector<BundleName> &bundleNames)
204 {
205 StartTrace(HITRACE_TAG_FILEMANAGEMENT, "RestoreApp");
206 if (!restore || !restore->session_) {
207 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
208 }
209 for (auto &bundleName : bundleNames) {
210 if (!regex_match(bundleName, regex("^[0-9a-zA-Z_.]+$"))) {
211 throw BError(BError::Codes::TOOL_INVAL_ARG, "bundleName is not alphanumeric");
212 }
213 string path = string(BConstants::BACKUP_TOOL_RECEIVE_DIR) + bundleName;
214 if (access(path.data(), F_OK) != 0) {
215 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
216 }
217 const auto [err, filePaths] = BDir::GetDirFiles(path);
218 if (err != 0) {
219 throw BError(BError::Codes::TOOL_INVAL_ARG, "error path");
220 }
221 // install bundle.hap
222 string installPath = string(BConstants::BACKUP_TOOL_INSTALL_DIR) + bundleName + ".hap";
223 if (access(installPath.data(), F_OK) == 0) {
224 printf("install bundle hap %s\n", installPath.c_str());
225 restore->session_->GetFileHandle(bundleName, string(BConstants::RESTORE_INSTALL_PATH));
226 }
227 for (auto &filePath : filePaths) {
228 string fileName = filePath.substr(filePath.rfind("/") + 1);
229 restore->session_->GetFileHandle(bundleName, fileName);
230 restore->UpdateBundleSendFiles(bundleName, fileName);
231 }
232 }
233 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
234 }
235
GetRealPath(string & path)236 static bool GetRealPath(string &path)
237 {
238 unique_ptr<char[]> absPath = make_unique<char[]>(PATH_MAX + 1);
239 if (realpath(path.c_str(), absPath.get()) == nullptr) {
240 return false;
241 }
242
243 path = absPath.get();
244 if (access(path.data(), F_OK) != 0) {
245 return false;
246 }
247
248 return true;
249 }
250
InitPathCapFile(const string & pathCapFile,vector<string> bundleNames)251 static int32_t InitPathCapFile(const string &pathCapFile, vector<string> bundleNames)
252 {
253 StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Init");
254 string realPath = pathCapFile;
255 if (!GetRealPath(realPath)) {
256 fprintf(stderr, "path to realpath error");
257 return -errno;
258 }
259
260 UniqueFd fd(open(realPath.data(), O_RDWR, S_IRWXU));
261 if (fd < 0) {
262 fprintf(stderr, "Failed to open file error: %d %s\n", errno, strerror(errno));
263 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
264 return -errno;
265 }
266 if (access(BConstants::BACKUP_TOOL_INSTALL_DIR.data(), F_OK) == 0) {
267 BJsonCachedEntity<BJsonEntityCaps> cachedEntity(move(fd));
268 auto cache = cachedEntity.Structuralize();
269 vector<BJsonEntityCaps::BundleInfo> bundleInfos;
270 for (auto name : bundleNames) {
271 string installPath = string(BConstants::BACKUP_TOOL_INSTALL_DIR) + name + ".hap";
272 if (access(installPath.data(), F_OK) == 0) {
273 bool needToInstall = true;
274 bundleInfos.emplace_back(BJsonEntityCaps::BundleInfo {.name = name, .needToInstall = needToInstall});
275 }
276 }
277 cache.SetBundleInfos(bundleInfos);
278 cachedEntity.Persist();
279 fd = move(cachedEntity.GetFd());
280 }
281 auto ctx = make_shared<Session>();
282 ctx->session_ = BSessionRestore::Init(
283 BSessionRestore::Callbacks {.onFileReady = bind(OnFileReady, ctx, placeholders::_1, placeholders::_2),
284 .onBundleStarted = bind(OnBundleStarted, ctx, placeholders::_1, placeholders::_2),
285 .onBundleFinished = bind(OnBundleFinished, ctx, placeholders::_1, placeholders::_2),
286 .onAllBundlesFinished = bind(OnAllBundlesFinished, ctx, placeholders::_1),
287 .onBackupServiceDied = bind(OnBackupServiceDied, ctx)});
288 if (ctx->session_ == nullptr) {
289 printf("Failed to init restore\n");
290 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
291 return -EPERM;
292 }
293 int ret = ctx->session_->AppendBundles(move(fd), bundleNames);
294 if (ret != 0) {
295 printf("restore append bundles error: %d\n", ret);
296 return -ret;
297 }
298 ctx->SetBundleFinishedCount(bundleNames.size());
299 RestoreApp(ctx, bundleNames);
300 ctx->Wait();
301 return 0;
302 }
303
Exec(map<string,vector<string>> & mapArgToVal)304 static int Exec(map<string, vector<string>> &mapArgToVal)
305 {
306 if (mapArgToVal.find("pathCapFile") == mapArgToVal.end() || mapArgToVal.find("bundles") == mapArgToVal.end()) {
307 return -EPERM;
308 }
309 return InitPathCapFile(*(mapArgToVal["pathCapFile"].begin()), mapArgToVal["bundles"]);
310 }
311
312 /**
313 * @brief The hack behind is that "variable with static storage duration has initialization or a destructor with side
314 * effects; it shall not be eliminated even if it appears to be unused" -- point 2.[basic.stc.static].c++ draft
315 *
316 */
317 static bool g_autoRegHack = ToolsOp::Register(ToolsOp {ToolsOp::Descriptor {
318 .opName = {"restore"},
319 .argList = {{
320 .paramName = "pathCapFile",
321 .repeatable = false,
322 },
323 {
324 .paramName = "bundles",
325 .repeatable = true,
326 }},
327 .funcGenHelpMsg = GenHelpMsg,
328 .funcExec = Exec,
329 }});
330 } // namespace OHOS::FileManagement::Backup