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