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/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 int ASSET_IN_BUCKET_NUM_MAX = 1000;
47 const int ASSET_DIR_START_NUM = 16;
48 }
49
50 struct MediaUriInfo {
51 string mediaType;
52 string fileId;
53 string realName;
54 string displayName;
55 };
56
57 std::unordered_map<std::string, std::string> SandboxHelper::sandboxPathMap_;
58 std::mutex SandboxHelper::mapMutex_;
59
Encode(const string & uri)60 string SandboxHelper::Encode(const string &uri)
61 {
62 const unordered_set<char> uriCompentsSet = {
63 ';', ',', '/', '?', ':', '@', '&',
64 '=', '+', '$', '-', '_', '.', '!',
65 '~', '*', '(', ')', '#', '\''
66 };
67 const int32_t encodeLen = 2;
68 ostringstream outPutStream;
69 outPutStream.fill('0');
70 outPutStream << std::hex;
71
72 for (unsigned char tmpChar : uri) {
73 if (std::isalnum(tmpChar) || uriCompentsSet.find(tmpChar) != uriCompentsSet.end()) {
74 outPutStream << tmpChar;
75 } else {
76 outPutStream << std::uppercase;
77 outPutStream << '%' << std::setw(encodeLen) << static_cast<unsigned int>(tmpChar);
78 outPutStream << std::nouppercase;
79 }
80 }
81
82 return outPutStream.str();
83 }
84
Decode(const string & uri)85 string SandboxHelper::Decode(const string &uri)
86 {
87 std::ostringstream outPutStream;
88 const int32_t encodeLen = 2;
89 size_t index = 0;
90 while (index < uri.length()) {
91 if (uri[index] == '%') {
92 int hex = 0;
93 std::istringstream inputStream(uri.substr(index + 1, encodeLen));
94 inputStream >> std::hex >> hex;
95 outPutStream << static_cast<char>(hex);
96 index += encodeLen + 1;
97 } else {
98 outPutStream << uri[index];
99 index++;
100 }
101 }
102
103 return outPutStream.str();
104 }
105
GetLowerPath(string & lowerPathHead,const string & lowerPathTail,const string & userId,const string & bundleName)106 static string GetLowerPath(string &lowerPathHead, const string &lowerPathTail,
107 const string &userId, const string &bundleName)
108 {
109 if (lowerPathHead.find(CURRENT_USER_ID_FLAG) != string::npos) {
110 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(CURRENT_USER_ID_FLAG),
111 CURRENT_USER_ID_FLAG.length(), userId);
112 }
113
114 if (lowerPathHead.find(PACKAGE_NAME_FLAG) != string::npos) {
115 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(PACKAGE_NAME_FLAG),
116 PACKAGE_NAME_FLAG.length(), bundleName);
117 }
118
119 return lowerPathHead + lowerPathTail;
120 }
121
GetSandboxPathMap()122 bool SandboxHelper::GetSandboxPathMap()
123 {
124 lock_guard<mutex> lock(mapMutex_);
125 if (sandboxPathMap_.size() > 0) {
126 return true;
127 }
128
129 nlohmann::json jsonObj;
130 int ret = JsonUtils::GetJsonObjFromPath(jsonObj, SANDBOX_JSON_FILE_PATH);
131 if (ret != 0) {
132 LOGE("Get json object failed from %{public}s with %{public}d", SANDBOX_JSON_FILE_PATH.c_str(), ret);
133 return false;
134 }
135
136 if (jsonObj.find(MOUNT_PATH_MAP_KEY) == jsonObj.end()) {
137 LOGE("Json object find mount path map failed");
138 return false;
139 }
140
141 nlohmann::json mountPathMap = jsonObj[MOUNT_PATH_MAP_KEY];
142 for (size_t i = 0; i < mountPathMap.size(); i++) {
143 string srcPath = mountPathMap[i][PHYSICAL_PATH_KEY];
144 string sandboxPath = mountPathMap[i][SANDBOX_PATH_KEY];
145 sandboxPathMap_[sandboxPath] = srcPath;
146 }
147
148 if (sandboxPathMap_.size() == 0) {
149 return false;
150 }
151
152 return true;
153 }
154
GetPathSuffix(const std::string & path,string & pathSuffix)155 static int32_t GetPathSuffix(const std::string &path, string &pathSuffix)
156 {
157 size_t pos = path.rfind('.');
158 if (pos != string::npos) {
159 pathSuffix = path.substr(pos);
160 return 0;
161 }
162 return -EINVAL;
163 }
164
CalAssetBucket(const int32_t & fileId)165 static int32_t CalAssetBucket(const int32_t &fileId)
166 {
167 int32_t bucketNum = 0;
168 if (fileId < 0) {
169 LOGE("input fileId %{private}d is invalid", fileId);
170 return -EINVAL;
171 }
172
173 int32_t start = ASSET_DIR_START_NUM;
174 int32_t divider = ASSET_DIR_START_NUM;
175 while (fileId > start * ASSET_IN_BUCKET_NUM_MAX) {
176 divider = start;
177 start <<= 1;
178 }
179
180 int32_t fileIdRemainder = fileId % divider;
181 if (fileIdRemainder == 0) {
182 bucketNum = start + fileIdRemainder;
183 } else {
184 bucketNum = (start - divider) + fileIdRemainder;
185 }
186 return bucketNum;
187 }
188
GetFileIdFromFileName(const std::string & fileName)189 static int32_t GetFileIdFromFileName(const std::string &fileName)
190 {
191 size_t pos = fileName.find_last_of('_');
192 if (pos == std::string::npos || pos == fileName.size() - 1) {
193 return -EINVAL;
194 }
195
196 std::string idStr = fileName.substr(pos + 1);
197 if (!std::all_of(idStr.begin(), idStr.end(), ::isdigit)) {
198 return -EINVAL;
199 }
200
201 return std::stoi(idStr);
202 }
203
GetBucketNum(const std::string & fileName)204 static int32_t GetBucketNum(const std::string &fileName)
205 {
206 int32_t fileId = GetFileIdFromFileName(fileName);
207 if (fileId < 0) {
208 LOGE("GetFileIdFromFileName failed with %{public}s", fileName.c_str());
209 return fileId;
210 }
211 return CalAssetBucket(fileId);
212 }
213
ParseMediaSandboxPath(const string & sandboxPath,MediaUriInfo & mediaUriInfo)214 static void ParseMediaSandboxPath(const string &sandboxPath, MediaUriInfo &mediaUriInfo)
215 {
216 string path = sandboxPath;
217 std::replace(path.begin(), path.end(), '/', ' ');
218 stringstream ss;
219 ss << path;
220 ss >> mediaUriInfo.mediaType >> mediaUriInfo.fileId >> mediaUriInfo.realName >> mediaUriInfo.displayName;
221 }
222
GetMediaPhysicalPath(const std::string & sandboxPath,const std::string & userId,std::string & physicalPath)223 static int32_t GetMediaPhysicalPath(const std::string &sandboxPath, const std::string &userId,
224 std::string &physicalPath)
225 {
226 MediaUriInfo mediaUriInfo;
227 ParseMediaSandboxPath(sandboxPath, mediaUriInfo);
228
229 int32_t bucketNum = GetBucketNum(mediaUriInfo.realName);
230 if (bucketNum < 0) {
231 return -EINVAL;
232 }
233
234 std::string mediaSuffix;
235 if (GetPathSuffix(sandboxPath, mediaSuffix) != 0) {
236 LOGE("GetPathSuffix failed");
237 return -EINVAL;
238 }
239
240 physicalPath = SHAER_PATH_HEAD + userId + SHAER_PATH_MID + mediaUriInfo.mediaType +
241 BACKFLASH + to_string(bucketNum) + BACKFLASH + mediaUriInfo.realName + mediaSuffix;
242 return 0;
243 }
244
GetPhysicalPath(const std::string & fileUri,const std::string & userId,std::string & physicalPath)245 int32_t SandboxHelper::GetPhysicalPath(const std::string &fileUri, const std::string &userId,
246 std::string &physicalPath)
247 {
248 Uri uri(fileUri);
249 string bundleName = uri.GetAuthority();
250 string sandboxPath = uri.GetPath();
251
252 if (bundleName == MEDIA) {
253 return GetMediaPhysicalPath(sandboxPath, userId, physicalPath);
254 }
255
256 if ((sandboxPath.find(FILE_MANAGER_URI_HEAD) == 0 && bundleName != FILE_MANAGER_AUTHORITY) ||
257 (sandboxPath.find(FUSE_URI_HEAD) == 0 && bundleName != DLP_MANAGER_BUNDLE_NAME)) {
258 return -EINVAL;
259 }
260
261 if (!GetSandboxPathMap()) {
262 LOGE("GetSandboxPathMap failed");
263 return -EINVAL;
264 }
265
266 string lowerPathTail = "";
267 string lowerPathHead = "";
268 string::size_type curPrefixMatchLen = 0;
269 for (auto it = sandboxPathMap_.begin(); it != sandboxPathMap_.end(); it++) {
270 string sandboxPathPrefix = it->first;
271 string::size_type prefixMatchLen = sandboxPathPrefix.length();
272 if (sandboxPath.length() >= prefixMatchLen) {
273 string sandboxPathTemp = sandboxPath.substr(0, prefixMatchLen);
274 if (sandboxPathTemp == sandboxPathPrefix && curPrefixMatchLen <= prefixMatchLen) {
275 curPrefixMatchLen = prefixMatchLen;
276 lowerPathHead = it->second;
277 lowerPathTail = sandboxPath.substr(prefixMatchLen);
278 }
279 }
280 }
281
282 if (lowerPathHead == "") {
283 LOGE("lowerPathHead is invalid");
284 return -EINVAL;
285 } else {
286 physicalPath = GetLowerPath(lowerPathHead, lowerPathTail, userId, bundleName);
287 return 0;
288 }
289 }
290
CheckValidPath(const std::string & filePath)291 bool SandboxHelper::CheckValidPath(const std::string &filePath)
292 {
293 if (filePath.empty() || filePath.size() >= PATH_MAX) {
294 return false;
295 }
296
297 char realPath[PATH_MAX]{'\0'};
298 if (realpath(filePath.c_str(), realPath) != nullptr &&
299 strncmp(realPath, filePath.c_str(), filePath.size()) == 0) {
300 return true;
301 } else {
302 return false;
303 }
304 }
305 } // namespace AppFileService
306 } // namespace OHOS
307
308