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 #include "file_share.h"
16
17 #include <dirent.h>
18 #include <fcntl.h>
19 #include <stack>
20 #include <sys/mount.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24
25 #include "accesstoken_kit.h"
26 #include "hap_token_info.h"
27 #include "log.h"
28 #include "uri.h"
29
30 namespace OHOS {
31 namespace AppFileService {
32
33 struct FileShareInfo {
34 string providerBundleName_;
35 string targetBundleName_;
36 string providerLowerPath_;
37 string providerSandboxPath_;
38 vector<string> sharePath_;
39 vector<bool> isExist_;
40 string currentUid_;
41 SHARE_FILE_TYPE type_;
42 };
43
GetTargetInfo(int32_t tokenId,string & bundleName,string & currentUid)44 static int32_t GetTargetInfo(int32_t tokenId, string &bundleName, string ¤tUid)
45 {
46 Security::AccessToken::HapTokenInfo hapInfo;
47 int32_t result = Security::AccessToken::AccessTokenKit::GetHapTokenInfo(tokenId, hapInfo);
48 if (result != 0) {
49 LOGE("Failed to get hap token info %{public}d", result);
50 return -EINVAL;
51 }
52 bundleName = hapInfo.bundleName;
53 currentUid = to_string(hapInfo.userID);
54 return 0;
55 }
56
GetProviderBundleName(string uriStr,string & bundleName)57 static void GetProviderBundleName(string uriStr, string &bundleName)
58 {
59 Uri uri(uriStr);
60 bundleName = uri.GetAuthority();
61 }
62
IsExistDir(const string & path)63 static bool IsExistDir(const string &path)
64 {
65 struct stat buf = {};
66 if (stat(path.c_str(), &buf) != 0) {
67 return false;
68 }
69 return S_ISDIR(buf.st_mode);
70 }
71
IsExistFile(const string & path)72 static bool IsExistFile(const string &path)
73 {
74 struct stat buf = {};
75 if (stat(path.c_str(), &buf) != 0) {
76 LOGE("Get path stat failed, path: %s err %{public}d", path.c_str(), errno);
77 return false;
78 }
79 return S_ISREG(buf.st_mode);
80 }
81
GetLowerPath(string & lowerPathHead,const string & lowerPathTail,FileShareInfo & info)82 static int32_t GetLowerPath(string &lowerPathHead, const string &lowerPathTail, FileShareInfo &info)
83 {
84 if (lowerPathHead.empty()) {
85 return -EINVAL;
86 }
87
88 if (lowerPathHead.find(CURRENT_USER_ID_FLAG) != string::npos) {
89 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(CURRENT_USER_ID_FLAG),
90 CURRENT_USER_ID_FLAG.length(), info.currentUid_);
91 }
92
93 if (lowerPathHead.find(PACKAGE_NAME_FLAG) != string::npos) {
94 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(PACKAGE_NAME_FLAG),
95 PACKAGE_NAME_FLAG.length(), info.providerBundleName_);
96 }
97
98 info.providerLowerPath_ = lowerPathHead + lowerPathTail;
99 return 0;
100 }
101
GetProviderPath(const string & uriStr,int32_t tokenId,FileShareInfo & info)102 static int32_t GetProviderPath(const string &uriStr, int32_t tokenId, FileShareInfo &info)
103 {
104 Uri uri(uriStr);
105 string pathInProvider = uri.GetPath();
106 string::size_type pos = pathInProvider.find(DATA_STORAGE_PATH);
107 if (pos == string::npos) {
108 return -EINVAL;
109 }
110
111 size_t num = SANDBOX_PATH.size();
112 string lowerPathTail = "", lowerPathHead = "";
113 for (size_t i = 0; i < num; i++) {
114 if (pathInProvider.length() >= SANDBOX_PATH[i].length()) {
115 string sandboxPathTemp = pathInProvider.substr(0, SANDBOX_PATH[i].length());
116 if (sandboxPathTemp == SANDBOX_PATH[i]) {
117 lowerPathHead = LOWER_PATH[i];
118 lowerPathTail = pathInProvider.substr(SANDBOX_PATH[i].length());
119 break;
120 }
121 }
122 }
123
124 int32_t ret = GetLowerPath(lowerPathHead, lowerPathTail, info);
125 if (ret != 0) {
126 LOGE("Get lower path failed with %{public}d", ret);
127 return ret;
128 }
129
130 info.providerSandboxPath_ = pathInProvider;
131 return 0;
132 }
133
GetSharePath(FileShareInfo & info,int32_t flag)134 static void GetSharePath(FileShareInfo &info, int32_t flag)
135 {
136 string shareRPath = DATA_APP_EL2_PATH + info.currentUid_ + SHARE_PATH +info.targetBundleName_ +
137 SHARE_R_PATH + info.providerBundleName_ + info.providerSandboxPath_;
138 string shareRWPath = DATA_APP_EL2_PATH + info.currentUid_ + SHARE_PATH +info.targetBundleName_ +
139 SHARE_RW_PATH + info.providerBundleName_ + info.providerSandboxPath_;
140 if ((flag & WRITE_URI_PERMISSION) == WRITE_URI_PERMISSION) {
141 info.sharePath_.push_back(shareRWPath);
142 info.sharePath_.push_back(shareRPath);
143 info.isExist_.push_back(false);
144 info.isExist_.push_back(false);
145 } else if ((flag & READ_URI_PERMISSION) == READ_URI_PERMISSION) {
146 info.sharePath_.push_back(shareRPath);
147 info.isExist_.push_back(false);
148 }
149 }
150
GetShareFileType(FileShareInfo & info)151 static int32_t GetShareFileType(FileShareInfo &info)
152 {
153 if (IsExistFile(info.providerLowerPath_)) {
154 info.type_ = FILE_TYPE;
155 return 0;
156 } else if (IsExistDir(info.providerLowerPath_)) {
157 info.type_ = DIR_TYPE;
158 return 0;
159 }
160 return -ENOENT;
161 }
162
GetFileShareInfo(const string & uri,int32_t tokenId,int32_t flag,FileShareInfo & info)163 static int32_t GetFileShareInfo(const string &uri, int32_t tokenId, int32_t flag, FileShareInfo &info)
164 {
165 int32_t ret = 0;
166 ret = GetTargetInfo(tokenId, info.targetBundleName_, info.currentUid_);
167 if (ret != 0) {
168 LOGE("Failed to get target info %{public}d", ret);
169 return ret;
170 }
171
172 GetProviderBundleName(uri, info.providerBundleName_);
173 ret = GetProviderPath(uri, tokenId, info);
174 if (ret != 0) {
175 LOGE("Failed to get lower path %{public}d", ret);
176 return ret;
177 }
178
179 ret = GetShareFileType(info);
180 if (ret != 0) {
181 LOGE("Failed to get share file type %{public}d", ret);
182 return ret;
183 }
184 GetSharePath(info, flag);
185 return 0;
186 }
187
MakeDir(const string & path)188 static bool MakeDir(const string &path)
189 {
190 string::size_type index = 0;
191 string subPath;
192 do {
193 index = path.find('/', index + 1);
194 if (index == string::npos) {
195 subPath = path;
196 } else {
197 subPath = path.substr(0, index);
198 }
199
200 if (access(subPath.c_str(), 0) != 0) {
201 if (mkdir(subPath.c_str(), DIR_MODE) != 0) {
202 LOGE("Failed to make dir with %{public}d", errno);
203 return false;
204 }
205 }
206 } while (index != string::npos);
207
208 return true;
209 }
210
RemoveDir(const string & path)211 static bool RemoveDir(const string& path)
212 {
213 stack<string> dirs, delDirs;
214 dirs.push(path);
215 while (dirs.size() > 0) {
216 string curPath = dirs.top();
217 dirs.pop();
218 DIR *curDir = opendir(curPath.c_str());
219 struct dirent *ptr = readdir(curDir);
220 while (ptr != nullptr) {
221 string subPath = curPath + "/" + string(ptr->d_name);
222 if (ptr->d_type == DT_DIR && strcmp(ptr->d_name, ".") != 0 &&
223 strcmp(ptr->d_name, "..") != 0) {
224 dirs.push(subPath);
225 delDirs.push(subPath);
226 } else if (ptr->d_type != DT_DIR && remove(subPath.c_str()) != 0) {
227 umount2(subPath.c_str(), MNT_DETACH);
228 remove(subPath.c_str());
229 LOGE("Failed to remove dir with %{public}d", errno);
230 }
231 ptr = readdir(curDir);
232 }
233 closedir(curDir);
234 }
235
236 while (delDirs.size() > 0) {
237 string curPath = delDirs.top();
238 if (remove(curPath.c_str()) != 0) {
239 LOGE("Failed to remove dir with %{public}d", errno);
240 }
241 delDirs.pop();
242 }
243
244 return true;
245 }
246
DeleteDir(const string & path)247 static bool DeleteDir(const string &path)
248 {
249 if (IsExistDir(path)) {
250 return RemoveDir(path);
251 }
252 return false;
253 }
254
PreparePreShareDir(FileShareInfo & info)255 static int32_t PreparePreShareDir(FileShareInfo &info)
256 {
257 for (size_t i = 0; i < info.sharePath_.size(); i++) {
258 if (access(info.sharePath_[i].c_str(), F_OK) != 0) {
259 string sharePathDir = info.sharePath_[i];
260 size_t posLast = info.sharePath_[i].find_last_of("/");
261 sharePathDir = info.sharePath_[i].substr(0, posLast);
262 if (!MakeDir(sharePathDir.c_str())) {
263 LOGE("Make dir failed with %{public}d", errno);
264 return -errno;
265 }
266 } else {
267 LOGE("File %{public}s already exists", info.sharePath_[i].c_str());
268 info.isExist_[i] = true;
269 }
270 }
271 return 0;
272 }
273
CreateShareFile(const string & uri,int32_t tokenId,int32_t flag)274 int32_t FileShare::CreateShareFile(const string &uri, int32_t tokenId, int32_t flag)
275 {
276 FileShareInfo info;
277 int32_t ret = GetFileShareInfo(uri, tokenId, flag, info);
278 if (ret != 0) {
279 LOGE("Failed to get FileShareInfo with %{public}d", ret);
280 return ret;
281 }
282
283 if ((ret = PreparePreShareDir(info)) != 0) {
284 LOGE("PreparePreShareDir failed");
285 return ret;
286 }
287
288 for (size_t i = 0; i < info.sharePath_.size(); i++) {
289 if (info.isExist_[i]) {
290 continue;
291 }
292
293 if (info.type_ == FILE_TYPE) {
294 if ((ret = creat(info.sharePath_[i].c_str(), FILE_MODE)) < 0) {
295 LOGE("Create file failed with %{public}d", errno);
296 return -errno;
297 }
298 close(ret);
299 } else if (mkdir(info.sharePath_[i].c_str(), DIR_MODE) != 0) {
300 LOGE("Create dir failed with %{public}d", errno);
301 return -errno;
302 }
303
304 if (mount(info.providerLowerPath_.c_str(), info.sharePath_[i].c_str(),
305 nullptr, MS_BIND, nullptr) != 0) {
306 LOGE("Mount failed with %{public}d", errno);
307 return -errno;
308 }
309 }
310 LOGI("Create Share File Successfully!");
311 return 0;
312 }
313
UmountDelUris(vector<string> sharePathList,string currentUid,string bundleNameSelf)314 static void UmountDelUris(vector<string> sharePathList, string currentUid, string bundleNameSelf)
315 {
316 for (size_t i = 0; i < sharePathList.size(); i++) {
317 Uri uri(sharePathList[i]);
318 string path = uri.GetPath();
319 string bundleName = uri.GetAuthority();
320 string delRPath = DATA_APP_EL2_PATH + currentUid + SHARE_PATH + bundleNameSelf + SHARE_R_PATH +
321 bundleName + path;
322 string delRWPath = DATA_APP_EL2_PATH + currentUid + SHARE_PATH + bundleNameSelf + SHARE_RW_PATH +
323 bundleName + path;
324 if (access(delRPath.c_str(), F_OK) == 0) {
325 if (umount2(delRPath.c_str(), MNT_DETACH) != 0) {
326 LOGE("UmountdelRPath, umount failed with %{public}d", errno);
327 }
328 }
329 if (access(delRWPath.c_str(), F_OK) == 0) {
330 if (umount2(delRWPath.c_str(), MNT_DETACH) != 0) {
331 LOGE("UmountdelRWPath, umount failed with %{public}d", errno);
332 }
333 }
334 }
335 }
336
DeleteShareFile(int32_t tokenId,vector<string> sharePathList)337 int32_t FileShare::DeleteShareFile(int32_t tokenId, vector<string> sharePathList)
338 {
339 string bundleName, currentUid;
340 int32_t ret = GetTargetInfo(tokenId, bundleName, currentUid);
341 if (ret != 0) {
342 LOGE("Failed to delete share file %{public}d", -EINVAL);
343 return -EINVAL;
344 }
345 UmountDelUris(sharePathList, currentUid, bundleName);
346
347 string sharePath = DATA_APP_EL2_PATH + currentUid + SHARE_PATH + bundleName;
348 if ((access(sharePath.c_str(), F_OK) == 0) && !DeleteDir(sharePath)) {
349 LOGE("Delete dir failed with %{public}d", errno);
350 return -errno;
351 }
352
353 LOGI("Delete Share File Successfully!");
354 return 0;
355 }
356 } // namespace AppFileService
357 } // namespace OHOS