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 "b_tarball/b_tarball_factory.h"
17 
18 #include <algorithm>
19 #include <climits>
20 #include <cstring>
21 #include <iostream>
22 #include <iterator>
23 #include <libgen.h>
24 #include <map>
25 #include <sstream>
26 #include <string>
27 #include <tuple>
28 #include <unistd.h>
29 
30 #include "b_error/b_error.h"
31 #include "b_error/b_excep_utils.h"
32 #include "b_tarball/b_tarball_cmdline.h"
33 
34 namespace OHOS::FileManagement::Backup {
35 using namespace std;
36 namespace {
37 const string UNTAT_ROOT = "/";
38 } // namespace
39 
40 /**
41  * @brief Verifying untar input parameters
42  *
43  * @param root Root directory for storing unpacked files
44  * An absolute path is required.
45  */
UntarFort(string_view root)46 static void UntarFort(string_view root)
47 {
48     auto resolvedPath = BExcepUltils::Canonicalize(root);
49     if (string_view(UNTAT_ROOT) != root) {
50         resolvedPath += UNTAT_ROOT;
51     }
52     if (string_view(resolvedPath) != root) {
53         throw BError(BError::Codes::UTILS_INVAL_TARBALL_ARG, "The root must be an existing canonicalized path");
54     }
55 }
56 
57 /**
58  * @brief Filtering tar input parameters
59  *
60  * @param tarballDir Directory where the package file is stored
61  * @param root Root directory of the file to be packed
62  * An absolute path is required.
63  * @param includes Path to be packed in the root directory.
64  * The relative path is required. If this parameter is not specified, all packages are packed by default.
65  * @param excludes The part that does not need to be packed in the path to be packed
66  * Requires a relative path. Can be used to exclude some subdirectories
67  * @return std::tuple<vector<string>, vector<string>> 返回合法的includes, excludes
68  */
TarFilter(string_view tarballDir,string_view root,const vector<string_view> & includes,const vector<string_view> & excludes)69 static tuple<vector<string>, vector<string>> TarFilter(string_view tarballDir,
70                                                        string_view root,
71                                                        const vector<string_view> &includes,
72                                                        const vector<string_view> &excludes)
73 {
74     auto resolvedPath = make_unique<char[]>(PATH_MAX);
75     if (!realpath(root.data(), resolvedPath.get()) || (string_view(resolvedPath.get()) != root)) {
76         throw BError(BError::Codes::UTILS_INVAL_TARBALL_ARG, "The root must be an existing canonicalized path");
77     }
78 
79     auto removeBackSlash = [](const string_view &arg) -> string {
80         if (arg.empty()) {
81             return "";
82         }
83 
84         size_t i = arg.size() - 1;
85         for (; i > 0; --i) {
86             if (arg[i] != '/') {
87                 break;
88             }
89         }
90         return {arg.data(), i + 1};
91     };
92 
93     vector<string> newExcludes;
94     for (auto &item : excludes) {
95         string str = removeBackSlash(item);
96         if (!str.empty()) {
97             newExcludes.push_back(str);
98         }
99     }
100 
101     return {{includes.begin(), includes.end()}, newExcludes};
102 }
103 
104 /**
105  * @brief 校验tarball路径,并将之拆分为路径和文件名
106  *
107  * @param tarballPath tarball全路径
108  * @return tuple<string, string> 路径和文件名
109  */
GetTarballDirAndName(string_view tarballPath)110 static tuple<string, string> GetTarballDirAndName(string_view tarballPath)
111 {
112     char *buf4Dir = strdup(tarballPath.data());
113     if (!buf4Dir) {
114         throw BError(BError::Codes::UTILS_INVAL_TARBALL_ARG, "Out of memory");
115     }
116     string tarballDir = dirname(buf4Dir);
117     free(buf4Dir);
118 
119     char *buf4Name = strdup(tarballPath.data());
120     if (!buf4Name) {
121         throw BError(BError::Codes::UTILS_INVAL_TARBALL_ARG, "Out of memory");
122     }
123     string tarballName = basename(buf4Name);
124     free(buf4Name);
125 
126     auto resolvedPath = make_unique<char[]>(PATH_MAX);
127     if (!realpath(tarballDir.data(), resolvedPath.get())) {
128         throw BError(BError::Codes::UTILS_INVAL_TARBALL_ARG, generic_category().message(errno));
129     }
130     if (auto canonicalizedTarballDir = string_view(resolvedPath.get()); canonicalizedTarballDir != tarballDir) {
131         throw BError(BError::Codes::UTILS_INVAL_TARBALL_ARG, "Tarball path differed after canonicalizing");
132     }
133     if (auto suffix = string_view(".tar");
134         tarballPath.length() <= suffix.length() ||
135         !equal(tarballPath.rbegin(), next(tarballPath.rbegin(), suffix.length()), suffix.rbegin(), suffix.rend())) {
136         throw BError(BError::Codes::UTILS_INVAL_TARBALL_ARG, "Tarball path didn't end with '.tar'");
137     }
138     return {tarballDir, tarballName};
139 }
140 
141 /**
142  * @brief 绑定命令行实现的打包器
143  *
144  * @param tarballDir taball路径
145  * @param tarballName taball文件名
146  * @return unique_ptr<BTarballFactory::Impl> 打包器实现,包括tar和untar两种方法
147  * @see GetTarballDirAndName
148  */
BindCmdline(string_view tarballDir,string_view tarballName)149 static unique_ptr<BTarballFactory::Impl> BindCmdline(string_view tarballDir, string_view tarballName)
150 {
151     auto ptr = make_shared<BTarballCmdline>(tarballDir, tarballName);
152 
153     return make_unique<BTarballFactory::Impl>(BTarballFactory::Impl {
154         .tar = bind(&BTarballCmdline::Tar, ptr, placeholders::_1, placeholders::_2, placeholders::_3),
155         .untar = bind(&BTarballCmdline::Untar, ptr, placeholders::_1),
156     });
157 }
158 
Create(string_view implType,string_view tarballPath)159 unique_ptr<BTarballFactory::Impl> BTarballFactory::Create(string_view implType, string_view tarballPath)
160 {
161     static map<string_view, function<unique_ptr<BTarballFactory::Impl>(string_view, string_view)>> mapType2Tarball = {
162         {"cmdline", BindCmdline},
163     };
164 
165     try {
166         auto [tarballDir, tarballName] = GetTarballDirAndName(tarballPath);
167         auto tarballImpl = mapType2Tarball.at(implType)(tarballDir, tarballName);
168         if (tarballImpl->tar) {
169             tarballImpl->tar = [tarballDir {string(tarballDir)}, tar {tarballImpl->tar}](
170                                    string_view root, vector<string_view> includes, vector<string_view> excludes) {
171                 auto [newIncludes, newExcludes] = TarFilter(tarballDir, root, includes, excludes);
172                 tar(root, {newIncludes.begin(), newIncludes.end()}, {newExcludes.begin(), newExcludes.end()});
173             };
174         }
175         if (tarballImpl->untar) {
176             tarballImpl->untar = [untar {tarballImpl->untar}](string_view root) {
177                 UntarFort(root);
178                 untar(root);
179             };
180         }
181         return tarballImpl;
182     } catch (const out_of_range &e) {
183         stringstream ss;
184         ss << "Unsupported implementation " << implType;
185         throw BError(BError::Codes::UTILS_INVAL_TARBALL_ARG, ss.str());
186     }
187 }
188 } // namespace OHOS::FileManagement::Backup