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 <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
OnBundleFinished(shared_ptr<Session> ctx,ErrCode err,const BundleName name)161 static void OnBundleFinished(shared_ptr<Session> ctx, ErrCode err, const BundleName name)
162 {
163 printf("BundleFinished errCode = %d, BundleName = %s\n", err, name.c_str());
164 ctx->UpdateBundleFinishedCount();
165 ctx->TryNotify();
166 }
167
OnAllBundlesFinished(shared_ptr<Session> ctx,ErrCode err)168 static void OnAllBundlesFinished(shared_ptr<Session> ctx, ErrCode err)
169 {
170 ctx->isAllBundelsFinished.store(true);
171 if (err == 0) {
172 printf("all bundles backup finished end\n");
173 } else {
174 printf("Failed to Unplanned Abort error: %d\n", err);
175 ctx->TryNotify(true);
176 return;
177 }
178 ctx->TryNotify();
179 }
180
OnBackupServiceDied(shared_ptr<Session> ctx)181 static void OnBackupServiceDied(shared_ptr<Session> ctx)
182 {
183 printf("backupServiceDied\n");
184 ctx->TryNotify(true);
185 }
186
BackupToolDirSoftlinkToBackupDir()187 static void BackupToolDirSoftlinkToBackupDir()
188 {
189 // 判断BConstants::BACKUP_TOOL_LINK_DIR 是否是软链接
190 if (access(BConstants::BACKUP_TOOL_LINK_DIR.data(), F_OK) == 0) {
191 struct stat inStat = {};
192 if (lstat(BConstants::BACKUP_TOOL_LINK_DIR.data(), &inStat) == -1) {
193 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
194 }
195
196 if ((inStat.st_mode & S_IFMT) == S_IFLNK) {
197 return;
198 }
199 // 非软连接删除重新创建
200 if (!ForceRemoveDirectory(BConstants::BACKUP_TOOL_LINK_DIR.data())) {
201 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
202 }
203 }
204
205 if (access(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(), F_OK) != 0 &&
206 mkdir(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(), S_IRWXU) != 0) {
207 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
208 }
209 if (symlink(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(),
210 BConstants::BACKUP_TOOL_LINK_DIR.data()) == -1) {
211 HILOGE("failed to create soft link file %{public}s errno : %{public}d",
212 BConstants::BACKUP_TOOL_LINK_DIR.data(), errno);
213 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
214 }
215 }
216
InitPathCapFile(const string & pathCapFile,vector<string> bundleNames)217 static int32_t InitPathCapFile(const string &pathCapFile, vector<string> bundleNames)
218 {
219 StartTrace(HITRACE_TAG_FILEMANAGEMENT, "InitPathCapFile");
220 // SELinux backup_tool工具/data/文件夹下创建文件夹 SA服务因root用户的自定义标签无写入权限 此处调整为软链接形式
221 BackupToolDirSoftlinkToBackupDir();
222
223 if (access((BConstants::BACKUP_TOOL_RECEIVE_DIR).data(), F_OK) != 0 &&
224 mkdir((BConstants::BACKUP_TOOL_RECEIVE_DIR).data(), S_IRWXU) != 0) {
225 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
226 }
227
228 UniqueFd fdLocal(open(pathCapFile.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRWXU));
229 if (fdLocal < 0) {
230 fprintf(stderr, "Failed to open file. error: %d %s\n", errno, strerror(errno));
231 return -EPERM;
232 }
233 auto proxy = ServiceProxy::GetInstance();
234 if (!proxy) {
235 fprintf(stderr, "Get an empty backup sa proxy\n");
236 return -EPERM;
237 }
238 BFile::SendFile(fdLocal, proxy->GetLocalCapabilities());
239
240 auto ctx = make_shared<Session>();
241 ctx->session_ = BSessionBackup::Init(
242 BSessionBackup::Callbacks {.onFileReady = bind(OnFileReady, ctx, placeholders::_1, placeholders::_2),
243 .onBundleStarted = bind(OnBundleStarted, ctx, placeholders::_1, placeholders::_2),
244 .onBundleFinished = bind(OnBundleFinished, ctx, placeholders::_1, placeholders::_2),
245 .onAllBundlesFinished = bind(OnAllBundlesFinished, ctx, placeholders::_1),
246 .onBackupServiceDied = bind(OnBackupServiceDied, ctx)});
247 if (ctx->session_ == nullptr) {
248 printf("Failed to init backup\n");
249 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
250 return -EPERM;
251 }
252 int ret = ctx->session_->AppendBundles(bundleNames);
253 if (ret != 0) {
254 printf("backup append bundles error: %d\n", ret);
255 throw BError(BError::Codes::TOOL_INVAL_ARG, "backup append bundles error");
256 }
257
258 ctx->SetBundleFinishedCount(bundleNames.size());
259 ctx->Wait();
260 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
261 return 0;
262 }
263
Exec(map<string,vector<string>> & mapArgToVal)264 static int Exec(map<string, vector<string>> &mapArgToVal)
265 {
266 if (mapArgToVal.find("pathCapFile") == mapArgToVal.end() || mapArgToVal.find("bundles") == mapArgToVal.end() ||
267 mapArgToVal.find("isLocal") == mapArgToVal.end()) {
268 return -EPERM;
269 }
270 return InitPathCapFile(*(mapArgToVal["pathCapFile"].begin()), mapArgToVal["bundles"]);
271 }
272
BackUpRegister()273 bool BackUpRegister()
274 {
275 return ToolsOp::Register(ToolsOp {ToolsOp::Descriptor {
276 .opName = {"backup"},
277 .argList = {{
278 .paramName = "pathCapFile",
279 .repeatable = false,
280 },
281 {
282 .paramName = "bundles",
283 .repeatable = true,
284 },
285 {
286 .paramName = "isLocal",
287 .repeatable = false,
288 }},
289 .funcGenHelpMsg = GenHelpMsg,
290 .funcExec = Exec,
291 }});
292 }
293 } // namespace OHOS::FileManagement::Backup