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