• 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 <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