1 /*
2 * Copyright (c) 2024 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 "tools_op_incremental_backup.h"
17
18 #include <atomic>
19 #include <cerrno>
20 #include <condition_variable>
21 #include <cstdint>
22 #include <cstring>
23 #include <functional>
24 #include <map>
25 #include <memory>
26 #include <mutex>
27 #include <regex>
28 #include <set>
29 #include <string>
30
31 #include <fcntl.h>
32 #include <sys/resource.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36
37 #include <directory_ex.h>
38
39 #include "b_error/b_error.h"
40 #include "b_filesystem/b_file.h"
41 #include "b_json/b_json_entity_ext_manage.h"
42 #include "b_resources/b_constants.h"
43 #include "backup_kit_inner.h"
44 #include "base/hiviewdfx/hitrace/interfaces/native/innerkits/include/hitrace_meter/hitrace_meter.h"
45 #include "service_proxy.h"
46 #include "tools_op.h"
47
48 namespace OHOS::FileManagement::Backup {
49 using namespace std;
50
51 class SessionBckup {
52 public:
UpdateBundleReceivedFiles(const BundleName & bundleName,const string & fileName)53 void UpdateBundleReceivedFiles(const BundleName &bundleName, const string &fileName)
54 {
55 lock_guard<mutex> lk(lock_);
56 bundleStatusMap_[bundleName].receivedFile.insert(fileName);
57 TryClearBundleOfMap(bundleName);
58 }
59
SetIndexFiles(const BundleName & bundleName,UniqueFd fd)60 void SetIndexFiles(const BundleName &bundleName, UniqueFd fd)
61 {
62 BJsonCachedEntity<BJsonEntityExtManage> cachedEntity(move(fd));
63 auto cache = cachedEntity.Structuralize();
64 lock_guard<mutex> lk(lock_);
65 bundleStatusMap_[bundleName].indexFile = cache.GetExtManage();
66 TryClearBundleOfMap(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<BIncrementalBackupSession> session_ = {};
98
99 private:
100 struct BundleStatus {
101 set<string> receivedFile;
102 set<string> indexFile;
103 };
104
TryClearBundleOfMap(const BundleName & bundleName)105 void TryClearBundleOfMap(const BundleName &bundleName)
106 {
107 if (bundleStatusMap_[bundleName].indexFile == bundleStatusMap_[bundleName].receivedFile) {
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 vector<BIncrementalData> lastIncrementalData {};
121 };
122
GenHelpMsg()123 static string GenHelpMsg()
124 {
125 return "\t\tThis operation helps to backup application data.\n"
126 "\t\t--isLocal\t\t This parameter should be true or flase; true: local backup false: others.\n"
127 "\t\t--pathCapFile\t\t This parameter should be the path of the capability file.\n"
128 "\t\t--bundle\t\t This parameter is bundleName.";
129 }
130
OnFileReady(shared_ptr<SessionBckup> ctx,const BFileInfo & fileInfo,UniqueFd fd,UniqueFd manifestFd)131 static void OnFileReady(shared_ptr<SessionBckup> ctx, const BFileInfo &fileInfo, UniqueFd fd, UniqueFd manifestFd)
132 {
133 printf("FileReady owner = %s, fileName = %s, fd = %d, manifestFd = %d\n", fileInfo.owner.c_str(),
134 fileInfo.fileName.c_str(), fd.Get(), manifestFd.Get());
135 string tmpPath = string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + fileInfo.owner;
136 if (access(tmpPath.data(), F_OK) != 0 && mkdir(tmpPath.data(), S_IRWXU) != 0) {
137 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
138 }
139 auto iter = find_if(ctx->lastIncrementalData.begin(), ctx->lastIncrementalData.end(),
140 [bundleName {fileInfo.owner}](const auto &data) { return bundleName == data.bundleName; });
141 if (iter == ctx->lastIncrementalData.end()) {
142 throw BError(BError::Codes::TOOL_INVAL_ARG);
143 }
144 tmpPath = tmpPath + "/" + to_string(iter->lastIncrementalTime);
145 if (access(tmpPath.data(), F_OK) != 0 && mkdir(tmpPath.data(), S_IRWXU) != 0) {
146 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
147 }
148 // 数据文件保存 路径拼接方式: /data/backup/incrementalreceived/bundleName/时间戳/incremental/文件名
149 string tmpDataPath = tmpPath + string(BConstants::BACKUP_TOOL_INCREMENTAL);
150 if (access(tmpDataPath.data(), F_OK) != 0 && mkdir(tmpDataPath.data(), S_IRWXU) != 0) {
151 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
152 }
153 UniqueFd fdLocal(open((tmpDataPath + "/" + fileInfo.fileName).data(), O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU));
154 if (fdLocal < 0) {
155 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
156 }
157 BFile::SendFile(fdLocal, fd);
158
159 // 简报文件保存 路径拼接方式: /data/backup/incrementalreceived/bundleName/时间戳/manifest/文件名.rp
160 string tmpmanifestPath = tmpPath + string(BConstants::BACKUP_TOOL_MANIFEST);
161 if (access(tmpmanifestPath.data(), F_OK) != 0 && mkdir(tmpmanifestPath.data(), S_IRWXU) != 0) {
162 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
163 }
164 UniqueFd fdManifest(
165 open((tmpmanifestPath + "/" + fileInfo.fileName + ".rp").data(), O_RDWR | O_CREAT | O_TRUNC, S_IRWXU));
166 if (fdManifest < 0) {
167 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
168 }
169 BFile::SendFile(fdManifest, manifestFd);
170 if (fileInfo.fileName == BConstants::EXT_BACKUP_MANAGE) {
171 string path = string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + fileInfo.owner +
172 string(BConstants::BACKUP_TOOL_MANIFEST).append(".rp");
173 UniqueFd fullDatFd(open(path.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRWXU));
174 if (fullDatFd < 0) {
175 HILOGE("Failed to open rp file = %{private}s, err = %{public}d", path.c_str(), errno);
176 return;
177 }
178 BFile::SendFile(fullDatFd, fdManifest);
179 ctx->SetIndexFiles(fileInfo.owner, move(fd));
180 } else {
181 ctx->UpdateBundleReceivedFiles(fileInfo.owner, fileInfo.fileName);
182 }
183 ctx->TryNotify();
184 }
185
OnBundleStarted(shared_ptr<SessionBckup> ctx,ErrCode err,const BundleName name)186 static void OnBundleStarted(shared_ptr<SessionBckup> ctx, ErrCode err, const BundleName name)
187 {
188 printf("BundleStarted errCode = %d, BundleName = %s\n", err, name.c_str());
189 if (err != 0) {
190 ctx->isAllBundelsFinished.store(true);
191 ctx->UpdateBundleFinishedCount();
192 ctx->TryNotify();
193 }
194 }
195
OnBundleFinished(shared_ptr<SessionBckup> ctx,ErrCode err,const BundleName name)196 static void OnBundleFinished(shared_ptr<SessionBckup> ctx, ErrCode err, const BundleName name)
197 {
198 printf("BundleFinished errCode = %d, BundleName = %s\n", err, name.c_str());
199 ctx->UpdateBundleFinishedCount();
200 ctx->TryNotify();
201 }
202
OnAllBundlesFinished(shared_ptr<SessionBckup> ctx,ErrCode err)203 static void OnAllBundlesFinished(shared_ptr<SessionBckup> ctx, ErrCode err)
204 {
205 ctx->isAllBundelsFinished.store(true);
206 if (err == 0) {
207 printf("all bundles backup finished end\n");
208 } else {
209 printf("Failed to Unplanned Abort error: %d\n", err);
210 ctx->TryNotify(true);
211 return;
212 }
213 ctx->TryNotify();
214 }
215
OnResultReport(shared_ptr<SessionBckup> ctx,const std::string & bundleName,const std::string & resultInfo)216 static void OnResultReport(shared_ptr<SessionBckup> ctx, const std::string &bundleName, const std::string &resultInfo)
217 {
218 printf("OnResultReport bundleName = %s, resultInfo = %s\n", bundleName.c_str(), resultInfo.c_str());
219 }
220
OnBackupServiceDied(shared_ptr<SessionBckup> ctx)221 static void OnBackupServiceDied(shared_ptr<SessionBckup> ctx)
222 {
223 printf("backupServiceDied\n");
224 ctx->TryNotify(true);
225 }
226
OnProcess(shared_ptr<SessionBckup> ctx,const std::string & bundleName,const std::string & processInfo)227 static void OnProcess(shared_ptr<SessionBckup> ctx, const std::string &bundleName, const std::string &processInfo)
228 {
229 printf("OnProcess bundleName = %s, processInfo = %s\n", bundleName.c_str(), processInfo.c_str());
230 }
231
BackupToolDirSoftlinkToBackupDir()232 static void BackupToolDirSoftlinkToBackupDir()
233 {
234 // 判断BConstants::BACKUP_TOOL_LINK_DIR 是否是软链接
235 if (access(BConstants::BACKUP_TOOL_LINK_DIR.data(), F_OK) == 0) {
236 struct stat inStat = {};
237 if (lstat(BConstants::BACKUP_TOOL_LINK_DIR.data(), &inStat) == -1) {
238 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
239 }
240
241 if ((inStat.st_mode & S_IFMT) == S_IFLNK) {
242 return;
243 }
244 // 非软连接删除重新创建
245 if (!ForceRemoveDirectory(BConstants::BACKUP_TOOL_LINK_DIR.data())) {
246 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
247 }
248 }
249
250 if (access(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(), F_OK) != 0 &&
251 mkdir(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(), S_IRWXU) != 0) {
252 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
253 }
254 if (symlink(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(),
255 BConstants::BACKUP_TOOL_LINK_DIR.data()) == -1) {
256 HILOGE("failed to create soft link file %{public}s errno : %{public}d",
257 BConstants::BACKUP_TOOL_LINK_DIR.data(), errno);
258 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
259 }
260 }
261
GetLocalCapabilitiesIncremental(shared_ptr<SessionBckup> ctx,const string & pathCapFile,const vector<string> & bundleNames,const vector<string> & times)262 static int GetLocalCapabilitiesIncremental(shared_ptr<SessionBckup> ctx,
263 const string &pathCapFile,
264 const vector<string> &bundleNames,
265 const vector<string> ×)
266 {
267 if (bundleNames.size() != times.size()) {
268 fprintf(stderr, "Inconsistent amounts of bundles and incrementalTime!\n");
269 return -EPERM;
270 }
271 UniqueFd fdLocal(open(pathCapFile.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRWXU));
272 if (fdLocal < 0) {
273 fprintf(stderr, "Failed to open file. error: %d %s\n", errno, strerror(errno));
274 return -EPERM;
275 }
276 auto proxy = ServiceProxy::GetInstance();
277 if (!proxy) {
278 fprintf(stderr, "Get an empty backup sa proxy\n");
279 return -EPERM;
280 }
281 int num = 0;
282 for (const auto &bundleName : bundleNames) {
283 BIncrementalData data;
284 data.bundleName = bundleName;
285 data.lastIncrementalTime = atoi(times[num].c_str());
286 ctx->lastIncrementalData.push_back(data);
287 num++;
288 }
289 UniqueFd fd = proxy->GetLocalCapabilitiesIncremental(ctx->lastIncrementalData);
290 if (fd < 0) {
291 fprintf(stderr, "error GetLocalCapabilitiesIncremental");
292 } else {
293 BFile::SendFile(fdLocal, fd);
294 }
295 return 0;
296 }
297
Init(const string & pathCapFile,const vector<string> & bundleNames,const vector<string> & times)298 static int32_t Init(const string &pathCapFile, const vector<string>& bundleNames, const vector<string>& times)
299 {
300 StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Init");
301 // SELinux backup_tool工具/data/文件夹下创建文件夹 SA服务因root用户的自定义标签无写入权限 此处调整为软链接形式
302 BackupToolDirSoftlinkToBackupDir();
303
304 if (access((BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR).data(), F_OK) != 0 &&
305 mkdir((BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR).data(), S_IRWXU) != 0) {
306 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
307 }
308
309 auto ctx = make_shared<SessionBckup>();
310 ctx->session_ = BIncrementalBackupSession::Init(BIncrementalBackupSession::Callbacks {
311 .onFileReady = bind(OnFileReady, ctx, placeholders::_1, placeholders::_2, placeholders::_3),
312 .onBundleStarted = bind(OnBundleStarted, ctx, placeholders::_1, placeholders::_2),
313 .onBundleFinished = bind(OnBundleFinished, ctx, placeholders::_1, placeholders::_2),
314 .onAllBundlesFinished = bind(OnAllBundlesFinished, ctx, placeholders::_1),
315 .onResultReport = bind(OnResultReport, ctx, placeholders::_1, placeholders::_2),
316 .onBackupServiceDied = bind(OnBackupServiceDied, ctx),
317 .onProcess = bind(OnProcess, ctx, placeholders::_1, placeholders::_2)});
318 if (ctx->session_ == nullptr) {
319 printf("Failed to init backup\n");
320 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
321 return -EPERM;
322 }
323
324 if (int ret = GetLocalCapabilitiesIncremental(ctx, pathCapFile, bundleNames, times); ret != 0) {
325 return ret;
326 }
327 for (auto &data : ctx->lastIncrementalData) {
328 string tmpPath = string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + data.bundleName;
329 if (access(tmpPath.data(), F_OK) != 0 && mkdir(tmpPath.data(), S_IRWXU) != 0) {
330 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
331 }
332 tmpPath = tmpPath + string(BConstants::BACKUP_TOOL_MANIFEST).append(".rp");
333 data.manifestFd = open(tmpPath.data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
334 }
335 int ret = ctx->session_->AppendBundles(ctx->lastIncrementalData);
336 if (ret != 0) {
337 printf("backup append bundles error: %d\n", ret);
338 throw BError(BError::Codes::TOOL_INVAL_ARG, "backup append bundles error");
339 }
340
341 ctx->SetBundleFinishedCount(bundleNames.size());
342 ctx->Wait();
343 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
344 ctx->session_->Release();
345 return 0;
346 }
347
g_exec(map<string,vector<string>> & mapArgToVal)348 static int g_exec(map<string, vector<string>> &mapArgToVal)
349 {
350 if (mapArgToVal.find("pathCapFile") == mapArgToVal.end() || mapArgToVal.find("bundles") == mapArgToVal.end() ||
351 mapArgToVal.find("incrementalTime") == mapArgToVal.end()) {
352 return -EPERM;
353 }
354 return Init(*(mapArgToVal["pathCapFile"].begin()), mapArgToVal["bundles"], mapArgToVal["incrementalTime"]);
355 }
356
IncrementalBackUpRegister()357 bool IncrementalBackUpRegister()
358 {
359 return ToolsOp::Register(ToolsOp {ToolsOp::Descriptor {
360 .opName = {"incrementalbackup"},
361 .argList = {{
362 .paramName = "pathCapFile",
363 .repeatable = false,
364 },
365 {
366 .paramName = "bundles",
367 .repeatable = true,
368 },
369 {
370 .paramName = "incrementalTime",
371 .repeatable = true,
372 }},
373 .funcGenHelpMsg = GenHelpMsg,
374 .funcExec = g_exec,
375 }});
376 }
377 } // namespace OHOS::FileManagement::Backup