1 /*
2 * Copyright (c) 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 "sandbox_helper.h"
17
18 #include <iomanip>
19 #include <sstream>
20 #include <unordered_set>
21 #include <vector>
22
23 #include "log.h"
24 #include "json_utils.h"
25 #include "uri.h"
26
27 using namespace std;
28
29 namespace OHOS {
30 namespace AppFileService {
31 namespace {
32 const string PACKAGE_NAME_FLAG = "<PackageName>";
33 const string CURRENT_USER_ID_FLAG = "<currentUserId>";
34 const string PHYSICAL_PATH_KEY = "src-path";
35 const string SANDBOX_PATH_KEY = "sandbox-path";
36 const string MOUNT_PATH_MAP_KEY = "mount-path-map";
37 const string SANDBOX_JSON_FILE_PATH = "/etc/app_file_service/file_share_sandbox.json";
38 const std::string SHAER_PATH_HEAD = "/mnt/hmdfs/";
39 const std::string SHAER_PATH_MID = "/account/cloud_merge_view/files/";
40 const string FILE_MANAGER_URI_HEAD = "/storage/";
41 const string FILE_MANAGER_AUTHORITY = "docs";
42 const string DLP_MANAGER_BUNDLE_NAME = "com.ohos.dlpmanager";
43 const string FUSE_URI_HEAD = "/mnt/data/fuse";
44 const string BACKFLASH = "/";
45 const string MEDIA = "media";
46 const string NETWORK_ID_FLAG = "<networkId>";
47 const string LOCAL = "local";
48 const int ASSET_IN_BUCKET_NUM_MAX = 1000;
49 const int ASSET_DIR_START_NUM = 16;
50 }
51
52 struct MediaUriInfo {
53 string mediaType;
54 string fileId;
55 string realName;
56 string displayName;
57 };
58
59 std::unordered_map<std::string, std::string> SandboxHelper::sandboxPathMap_;
60 std::mutex SandboxHelper::mapMutex_;
61
Encode(const string & uri)62 string SandboxHelper::Encode(const string &uri)
63 {
64 const unordered_set<char> uriCompentsSet = {
65 '/', '-', '_', '.', '!',
66 '~', '*', '(', ')', '\''
67 };
68 const int32_t encodeLen = 2;
69 ostringstream outPutStream;
70 outPutStream.fill('0');
71 outPutStream << std::hex;
72
73 for (unsigned char tmpChar : uri) {
74 if (std::isalnum(tmpChar) || uriCompentsSet.find(tmpChar) != uriCompentsSet.end()) {
75 outPutStream << tmpChar;
76 } else {
77 outPutStream << std::uppercase;
78 outPutStream << '%' << std::setw(encodeLen) << static_cast<unsigned int>(tmpChar);
79 outPutStream << std::nouppercase;
80 }
81 }
82
83 return outPutStream.str();
84 }
85
Decode(const string & uri)86 string SandboxHelper::Decode(const string &uri)
87 {
88 std::ostringstream outPutStream;
89 const int32_t encodeLen = 2;
90 size_t index = 0;
91 while (index < uri.length()) {
92 if (uri[index] == '%') {
93 int hex = 0;
94 std::istringstream inputStream(uri.substr(index + 1, encodeLen));
95 inputStream >> std::hex >> hex;
96 outPutStream << static_cast<char>(hex);
97 index += encodeLen + 1;
98 } else {
99 outPutStream << uri[index];
100 index++;
101 }
102 }
103
104 return outPutStream.str();
105 }
106
GetLowerPath(string & lowerPathHead,const string & lowerPathTail,const string & userId,const string & bundleName,const string & networkId)107 static string GetLowerPath(string &lowerPathHead, const string &lowerPathTail,
108 const string &userId, const string &bundleName,
109 const string &networkId)
110 {
111 if (lowerPathHead.find(CURRENT_USER_ID_FLAG) != string::npos) {
112 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(CURRENT_USER_ID_FLAG),
113 CURRENT_USER_ID_FLAG.length(), userId);
114 }
115
116 if (lowerPathHead.find(PACKAGE_NAME_FLAG) != string::npos) {
117 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(PACKAGE_NAME_FLAG),
118 PACKAGE_NAME_FLAG.length(), bundleName);
119 }
120
121 if (lowerPathHead.find(NETWORK_ID_FLAG) != string::npos) {
122 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(NETWORK_ID_FLAG),
123 NETWORK_ID_FLAG.length(), networkId);
124 }
125
126 return lowerPathHead + lowerPathTail;
127 }
128
GetSandboxPathMap()129 bool SandboxHelper::GetSandboxPathMap()
130 {
131 lock_guard<mutex> lock(mapMutex_);
132 if (sandboxPathMap_.size() > 0) {
133 return true;
134 }
135
136 nlohmann::json jsonObj;
137 int ret = JsonUtils::GetJsonObjFromPath(jsonObj, SANDBOX_JSON_FILE_PATH);
138 if (ret != 0) {
139 LOGE("Get json object failed with %{public}d", ret);
140 return false;
141 }
142
143 if (jsonObj.find(MOUNT_PATH_MAP_KEY) == jsonObj.end()) {
144 LOGE("Json object find mount path map failed");
145 return false;
146 }
147
148 nlohmann::json mountPathMap = jsonObj[MOUNT_PATH_MAP_KEY];
149 for (size_t i = 0; i < mountPathMap.size(); i++) {
150 string srcPath = mountPathMap[i][PHYSICAL_PATH_KEY];
151 string sandboxPath = mountPathMap[i][SANDBOX_PATH_KEY];
152 sandboxPathMap_[sandboxPath] = srcPath;
153 }
154
155 if (sandboxPathMap_.size() == 0) {
156 return false;
157 }
158
159 return true;
160 }
161
GetPathSuffix(const std::string & path,string & pathSuffix)162 static int32_t GetPathSuffix(const std::string &path, string &pathSuffix)
163 {
164 size_t pos = path.rfind('.');
165 if (pos != string::npos) {
166 pathSuffix = path.substr(pos);
167 return 0;
168 }
169 return -EINVAL;
170 }
171
CalAssetBucket(const int32_t & fileId)172 static int32_t CalAssetBucket(const int32_t &fileId)
173 {
174 int32_t bucketNum = 0;
175 if (fileId < 0) {
176 LOGE("input fileId %{private}d is invalid", fileId);
177 return -EINVAL;
178 }
179
180 int32_t start = ASSET_DIR_START_NUM;
181 int32_t divider = ASSET_DIR_START_NUM;
182 while (fileId > start * ASSET_IN_BUCKET_NUM_MAX) {
183 divider = start;
184 start <<= 1;
185 }
186
187 int32_t fileIdRemainder = fileId % divider;
188 if (fileIdRemainder == 0) {
189 bucketNum = start + fileIdRemainder;
190 } else {
191 bucketNum = (start - divider) + fileIdRemainder;
192 }
193 return bucketNum;
194 }
195
GetFileIdFromFileName(const std::string & fileName)196 static int32_t GetFileIdFromFileName(const std::string &fileName)
197 {
198 size_t pos = fileName.find_last_of('_');
199 if (pos == std::string::npos || pos == fileName.size() - 1) {
200 return -EINVAL;
201 }
202
203 std::string idStr = fileName.substr(pos + 1);
204 if (!std::all_of(idStr.begin(), idStr.end(), ::isdigit)) {
205 return -EINVAL;
206 }
207
208 return std::stoi(idStr);
209 }
210
GetBucketNum(const std::string & fileName)211 static int32_t GetBucketNum(const std::string &fileName)
212 {
213 int32_t fileId = GetFileIdFromFileName(fileName);
214 if (fileId < 0) {
215 LOGE("GetFileIdFromFileName failed with %{public}s", fileName.c_str());
216 return fileId;
217 }
218 return CalAssetBucket(fileId);
219 }
220
ParseMediaSandboxPath(const string & sandboxPath,MediaUriInfo & mediaUriInfo)221 static bool ParseMediaSandboxPath(const string &sandboxPath, MediaUriInfo &mediaUriInfo)
222 {
223 string path = sandboxPath;
224 std::replace(path.begin(), path.end(), '/', ' ');
225 stringstream ss;
226 ss << path;
227 ss >> mediaUriInfo.mediaType >> mediaUriInfo.fileId >> mediaUriInfo.realName >> mediaUriInfo.displayName;
228
229 string buf;
230 ss >> buf;
231 if (!buf.empty()) {
232 LOGE("media sandboxPath is invalid");
233 return false;
234 }
235
236 return true;
237 }
238
GetMediaPhysicalPath(const std::string & sandboxPath,const std::string & userId,std::string & physicalPath)239 static int32_t GetMediaPhysicalPath(const std::string &sandboxPath, const std::string &userId,
240 std::string &physicalPath)
241 {
242 MediaUriInfo mediaUriInfo;
243 if (!ParseMediaSandboxPath(sandboxPath, mediaUriInfo)) {
244 return -EINVAL;
245 }
246
247 int32_t bucketNum = GetBucketNum(mediaUriInfo.realName);
248 if (bucketNum < 0) {
249 return -EINVAL;
250 }
251
252 std::string mediaSuffix;
253 if (GetPathSuffix(sandboxPath, mediaSuffix) != 0) {
254 LOGE("GetPathSuffix failed");
255 return -EINVAL;
256 }
257
258 physicalPath = SHAER_PATH_HEAD + userId + SHAER_PATH_MID + mediaUriInfo.mediaType +
259 BACKFLASH + to_string(bucketNum) + BACKFLASH + mediaUriInfo.realName + mediaSuffix;
260 return 0;
261 }
262
GetNetworkIdFromUri(const std::string & fileUri,string & networkId)263 static void GetNetworkIdFromUri(const std::string &fileUri, string &networkId)
264 {
265 Uri uri(fileUri);
266 std::string networkIdInfo = uri.GetQuery();
267 if (networkIdInfo.empty()) {
268 return;
269 }
270
271 size_t posIndex = networkIdInfo.find('=');
272 if (posIndex == string::npos || posIndex == (networkIdInfo.size() - 1)) {
273 return;
274 }
275 networkId = networkIdInfo.substr(posIndex + 1);
276 }
277
GetPhysicalPath(const std::string & fileUri,const std::string & userId,std::string & physicalPath)278 int32_t SandboxHelper::GetPhysicalPath(const std::string &fileUri, const std::string &userId,
279 std::string &physicalPath)
280 {
281 Uri uri(fileUri);
282 string bundleName = uri.GetAuthority();
283 if (bundleName == MEDIA) {
284 return GetMediaPhysicalPath(uri.GetPath(), userId, physicalPath);
285 }
286
287 string sandboxPath = SandboxHelper::Decode(uri.GetPath());
288 if ((sandboxPath.find(FILE_MANAGER_URI_HEAD) == 0 && bundleName != FILE_MANAGER_AUTHORITY) ||
289 (sandboxPath.find(FUSE_URI_HEAD) == 0 && bundleName != DLP_MANAGER_BUNDLE_NAME)) {
290 return -EINVAL;
291 }
292
293 if (!GetSandboxPathMap()) {
294 LOGE("GetSandboxPathMap failed");
295 return -EINVAL;
296 }
297
298 string lowerPathTail = "";
299 string lowerPathHead = "";
300 string::size_type curPrefixMatchLen = 0;
301 for (auto it = sandboxPathMap_.begin(); it != sandboxPathMap_.end(); it++) {
302 string sandboxPathPrefix = it->first;
303 string::size_type prefixMatchLen = sandboxPathPrefix.length();
304 if (sandboxPath.length() >= prefixMatchLen) {
305 string sandboxPathTemp = sandboxPath.substr(0, prefixMatchLen);
306 if (sandboxPathTemp == sandboxPathPrefix && curPrefixMatchLen <= prefixMatchLen) {
307 curPrefixMatchLen = prefixMatchLen;
308 lowerPathHead = it->second;
309 lowerPathTail = sandboxPath.substr(prefixMatchLen);
310 }
311 }
312 }
313
314 if (lowerPathHead == "") {
315 LOGE("lowerPathHead is invalid");
316 return -EINVAL;
317 }
318
319 string networkId = LOCAL;
320 GetNetworkIdFromUri(fileUri, networkId);
321
322 physicalPath = GetLowerPath(lowerPathHead, lowerPathTail, userId, bundleName, networkId);
323 return 0;
324 }
325
IsValidPath(const std::string & path)326 bool SandboxHelper::IsValidPath(const std::string &path)
327 {
328 if (path.find("/./") != std::string::npos ||
329 path.find("/../") != std::string::npos) {
330 return false;
331 }
332 return true;
333 }
334
CheckValidPath(const std::string & filePath)335 bool SandboxHelper::CheckValidPath(const std::string &filePath)
336 {
337 if (filePath.empty() || filePath.size() >= PATH_MAX) {
338 return false;
339 }
340
341 char realPath[PATH_MAX]{'\0'};
342 if (realpath(filePath.c_str(), realPath) != nullptr &&
343 strncmp(realPath, filePath.c_str(), filePath.size()) == 0) {
344 return true;
345 } else {
346 return false;
347 }
348 }
349 } // namespace AppFileService
350 } // namespace OHOS
351
352