1 /*
2  * Copyright (c) 2022-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 <atomic>
17 #include <cerrno>
18 #include <condition_variable>
19 #include <cstdint>
20 #include <cstring>
21 #include <functional>
22 #include <map>
23 #include <memory>
24 #include <mutex>
25 #include <regex>
26 #include <set>
27 #include <string>
28 
29 #include <fcntl.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 
34 #include <directory_ex.h>
35 
36 #include "b_error/b_error.h"
37 #include "b_filesystem/b_file.h"
38 #include "b_json/b_json_entity_ext_manage.h"
39 #include "b_resources/b_constants.h"
40 #include "backup_kit_inner.h"
41 #include "base/hiviewdfx/hitrace/interfaces/native/innerkits/include/hitrace_meter/hitrace_meter.h"
42 #include "service_proxy.h"
43 #include "tools_op.h"
44 #include "tools_op_backup.h"
45 
46 namespace OHOS::FileManagement::Backup {
47 using namespace std;
48 
49 class Session {
50 public:
UpdateBundleReceivedFiles(const BundleName & bundleName,const string & fileName)51     void UpdateBundleReceivedFiles(const BundleName &bundleName, const string &fileName)
52     {
53         lock_guard<mutex> lk(lock_);
54         bundleStatusMap_[bundleName].receivedFile.insert(fileName);
55         TryClearBundleOfMap(bundleName);
56     }
57 
SetIndexFiles(const BundleName & bundleName,UniqueFd fd)58     void SetIndexFiles(const BundleName &bundleName, UniqueFd fd)
59     {
60         BJsonCachedEntity<BJsonEntityExtManage> cachedEntity(move(fd));
61         auto cache = cachedEntity.Structuralize();
62         lock_guard<mutex> lk(lock_);
63         bundleStatusMap_[bundleName].indexFile = cache.GetExtManage();
64     }
65 
TryNotify(bool flag=false)66     void TryNotify(bool flag = false)
67     {
68         if (flag == true) {
69             ready_ = true;
70             cv_.notify_all();
71         } else if (bundleStatusMap_.size() == 0 && cnt_ == 0 && isAllBundelsFinished.load()) {
72             ready_ = true;
73             cv_.notify_all();
74         }
75     }
76 
UpdateBundleFinishedCount()77     void UpdateBundleFinishedCount()
78     {
79         lock_guard<mutex> lk(lock_);
80         cnt_--;
81     }
82 
SetBundleFinishedCount(uint32_t cnt)83     void SetBundleFinishedCount(uint32_t cnt)
84     {
85         cnt_ = cnt;
86     }
87 
Wait()88     void Wait()
89     {
90         unique_lock<mutex> lk(lock_);
91         cv_.wait(lk, [&] { return ready_; });
92     }
93 
94     unique_ptr<BSessionBackup> session_ = {};
95 
96 private:
97     struct BundleStatus {
98         set<string> receivedFile;
99         set<string> indexFile;
100     };
101 
TryClearBundleOfMap(const BundleName & bundleName)102     void TryClearBundleOfMap(const BundleName &bundleName)
103     {
104         if (bundleStatusMap_[bundleName].indexFile == bundleStatusMap_[bundleName].receivedFile) {
105             bundleStatusMap_.erase(bundleName);
106         }
107     }
108 
109     map<string, BundleStatus> bundleStatusMap_;
110     mutable condition_variable cv_;
111     mutex lock_;
112     bool ready_ = false;
113     uint32_t cnt_ {0};
114 
115 public:
116     std::atomic<bool> isAllBundelsFinished {false};
117 };
118 
GenHelpMsg()119 static string GenHelpMsg()
120 {
121     return "\t\tThis operation helps to backup application data.\n"
122            "\t\t--isLocal\t\t This parameter should be true or flase; true: local backup false: others.\n"
123            "\t\t--pathCapFile\t\t This parameter should be the path of the capability file.\n"
124            "\t\t--bundle\t\t This parameter is bundleName.";
125 }
126 
OnFileReady(shared_ptr<Session> ctx,const BFileInfo & fileInfo,UniqueFd fd)127 static void OnFileReady(shared_ptr<Session> ctx, const BFileInfo &fileInfo, UniqueFd fd)
128 {
129     printf("FileReady owner = %s, fileName = %s, sn = %u, fd = %d\n", fileInfo.owner.c_str(), fileInfo.fileName.c_str(),
130            fileInfo.sn, fd.Get());
131     string tmpPath = string(BConstants::BACKUP_TOOL_RECEIVE_DIR) + fileInfo.owner;
132     if (access(tmpPath.data(), F_OK) != 0 && mkdir(tmpPath.data(), S_IRWXU) != 0) {
133         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
134     }
135     if (fileInfo.fileName.find('/') != string::npos) {
136         throw BError(BError::Codes::TOOL_INVAL_ARG, "Filename is not valid");
137     }
138     UniqueFd fdLocal(open((tmpPath + "/" + fileInfo.fileName).data(), O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU));
139     if (fdLocal < 0) {
140         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
141     }
142     BFile::SendFile(fdLocal, fd);
143     if (fileInfo.fileName == BConstants::EXT_BACKUP_MANAGE) {
144         ctx->SetIndexFiles(fileInfo.owner, move(fd));
145     } else {
146         ctx->UpdateBundleReceivedFiles(fileInfo.owner, fileInfo.fileName);
147     }
148     ctx->TryNotify();
149 }
150 
OnBundleStarted(shared_ptr<Session> ctx,ErrCode err,const BundleName name)151 static void OnBundleStarted(shared_ptr<Session> ctx, ErrCode err, const BundleName name)
152 {
153     printf("BundleStarted errCode = %d, BundleName = %s\n", err, name.c_str());
154     if (err != 0) {
155         ctx->isAllBundelsFinished.store(true);
156         ctx->UpdateBundleFinishedCount();
157         ctx->TryNotify();
158     }
159 }
160 
OnResultReport(shared_ptr<Session> ctx,const std::string bundleName,const std::string result)161 static void OnResultReport(shared_ptr<Session> ctx, const std::string bundleName, const std::string result)
162 {
163     printf("OnResultReport bundleName = %s, result = %s\n", bundleName.c_str(), result.c_str());
164 }
165 
OnBundleFinished(shared_ptr<Session> ctx,ErrCode err,const BundleName name)166 static void OnBundleFinished(shared_ptr<Session> ctx, ErrCode err, const BundleName name)
167 {
168     printf("BundleFinished errCode = %d, BundleName = %s\n", err, name.c_str());
169     ctx->UpdateBundleFinishedCount();
170     ctx->TryNotify();
171 }
172 
OnAllBundlesFinished(shared_ptr<Session> ctx,ErrCode err)173 static void OnAllBundlesFinished(shared_ptr<Session> ctx, ErrCode err)
174 {
175     ctx->isAllBundelsFinished.store(true);
176     if (err == 0) {
177         printf("all bundles backup finished end\n");
178     } else {
179         printf("Failed to Unplanned Abort error: %d\n", err);
180         ctx->TryNotify(true);
181         return;
182     }
183     ctx->TryNotify();
184 }
185 
OnBackupServiceDied(shared_ptr<Session> ctx)186 static void OnBackupServiceDied(shared_ptr<Session> ctx)
187 {
188     printf("backupServiceDied\n");
189     ctx->TryNotify(true);
190 }
191 
OnProcess(shared_ptr<Session> ctx,const std::string bundleName,const std::string processInfo)192 static void OnProcess(shared_ptr<Session> ctx, const std::string bundleName, const std::string processInfo)
193 {
194     printf("OnProcess bundleName = %s, result = %s\n", bundleName.c_str(), processInfo.c_str());
195 }
196 
BackupToolDirSoftlinkToBackupDir()197 static void BackupToolDirSoftlinkToBackupDir()
198 {
199     // 判断BConstants::BACKUP_TOOL_LINK_DIR 是否是软链接
200     if (access(BConstants::BACKUP_TOOL_LINK_DIR.data(), F_OK) == 0) {
201         struct stat inStat = {};
202         if (lstat(BConstants::BACKUP_TOOL_LINK_DIR.data(), &inStat) == -1) {
203             throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
204         }
205 
206         if ((inStat.st_mode & S_IFMT) == S_IFLNK) {
207             return;
208         }
209         // 非软连接删除重新创建
210         if (!ForceRemoveDirectory(BConstants::BACKUP_TOOL_LINK_DIR.data())) {
211             throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
212         }
213     }
214 
215     if (access(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(), F_OK) != 0 &&
216         mkdir(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(), S_IRWXU) != 0) {
217         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
218     }
219     if (symlink(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(),
220                 BConstants::BACKUP_TOOL_LINK_DIR.data()) == -1) {
221         HILOGE("failed to create soft link file %{public}s  errno : %{public}d",
222                BConstants::BACKUP_TOOL_LINK_DIR.data(), errno);
223         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
224     }
225 }
226 
InitPathCapFile(const string & pathCapFile,vector<string> bundleNames)227 static int32_t InitPathCapFile(const string &pathCapFile, vector<string> bundleNames)
228 {
229     StartTrace(HITRACE_TAG_FILEMANAGEMENT, "InitPathCapFile");
230     // SELinux backup_tool工具/data/文件夹下创建文件夹 SA服务因root用户的自定义标签无写入权限 此处调整为软链接形式
231     BackupToolDirSoftlinkToBackupDir();
232 
233     if (access((BConstants::BACKUP_TOOL_RECEIVE_DIR).data(), F_OK) != 0 &&
234         mkdir((BConstants::BACKUP_TOOL_RECEIVE_DIR).data(), S_IRWXU) != 0) {
235         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
236     }
237 
238     UniqueFd fdLocal(open(pathCapFile.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRWXU));
239     if (fdLocal < 0) {
240         fprintf(stderr, "Failed to open file. error: %d %s\n", errno, strerror(errno));
241         return -EPERM;
242     }
243     auto proxy = ServiceProxy::GetInstance();
244     if (!proxy) {
245         fprintf(stderr, "Get an empty backup sa proxy\n");
246         return -EPERM;
247     }
248     BFile::SendFile(fdLocal, proxy->GetLocalCapabilities());
249 
250     auto ctx = make_shared<Session>();
251     ctx->session_ = BSessionBackup::Init(
252         BSessionBackup::Callbacks {.onFileReady = bind(OnFileReady, ctx, placeholders::_1, placeholders::_2),
253                                    .onBundleStarted = bind(OnBundleStarted, ctx, placeholders::_1, placeholders::_2),
254                                    .onBundleFinished = bind(OnBundleFinished, ctx, placeholders::_1, placeholders::_2),
255                                    .onAllBundlesFinished = bind(OnAllBundlesFinished, ctx, placeholders::_1),
256                                    .onResultReport = bind(OnResultReport, ctx, placeholders::_1, placeholders::_2),
257                                    .onBackupServiceDied = bind(OnBackupServiceDied, ctx),
258                                    .onProcess = bind(OnProcess, ctx, placeholders::_1, placeholders::_2)});
259     if (ctx->session_ == nullptr) {
260         printf("Failed to init backup\n");
261         FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
262         return -EPERM;
263     }
264     ctx->SetBundleFinishedCount(bundleNames.size());
265     int ret = ctx->session_->AppendBundles(bundleNames);
266     if (ret != 0) {
267         printf("backup append bundles error: %d\n", ret);
268         throw BError(BError::Codes::TOOL_INVAL_ARG, "backup append bundles error");
269     }
270     ctx->Wait();
271     FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
272     ctx->session_->Release();
273     return 0;
274 }
275 
Exec(map<string,vector<string>> & mapArgToVal)276 static int Exec(map<string, vector<string>> &mapArgToVal)
277 {
278     if (mapArgToVal.find("pathCapFile") == mapArgToVal.end() || mapArgToVal.find("bundles") == mapArgToVal.end() ||
279         mapArgToVal.find("isLocal") == mapArgToVal.end()) {
280         return -EPERM;
281     }
282     return InitPathCapFile(*(mapArgToVal["pathCapFile"].begin()), mapArgToVal["bundles"]);
283 }
284 
BackUpRegister()285 bool BackUpRegister()
286 {
287     return ToolsOp::Register(ToolsOp {ToolsOp::Descriptor {
288         .opName = {"backup"},
289         .argList = {{
290                         .paramName = "pathCapFile",
291                         .repeatable = false,
292                     },
293                     {
294                         .paramName = "bundles",
295                         .repeatable = true,
296                     },
297                     {
298                         .paramName = "isLocal",
299                         .repeatable = false,
300                     }},
301         .funcGenHelpMsg = GenHelpMsg,
302         .funcExec = Exec,
303     }});
304 }
305 } // namespace OHOS::FileManagement::Backup