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 <fstream>
20 #include <stack>
21 #include <sys/mount.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include "accesstoken_kit.h"
27 #include "hap_token_info.h"
28 #include "log.h"
29 #include "sandbox_helper.h"
30 #include "uri.h"
31
32 namespace OHOS {
33 namespace AppFileService {
34 #define DIR_MODE (S_IRWXU | S_IXGRP | S_IXOTH)
35 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
36 #define READ_URI_PERMISSION OHOS::AAFwk::Want::FLAG_AUTH_READ_URI_PERMISSION
37 #define WRITE_URI_PERMISSION OHOS::AAFwk::Want::FLAG_AUTH_WRITE_URI_PERMISSION
38 #define PERSISTABLE_URI_PERMISSION OHOS::AAFwk::Want::FLAG_AUTH_PERSISTABLE_URI_PERMISSION
39
40 enum ShareFileType {
41 DIR_TYPE = 0,
42 FILE_TYPE = 1,
43 };
44
45 namespace {
46 const string BROKER_SCHEME_PREFIX = "content://";
47 const string DATA_APP_EL2_PATH = "/data/service/el2/";
48 const string SHARE_R_PATH = "/r/";
49 const string SHARE_RW_PATH = "/rw/";
50 const string SHARE_PATH = "/share/";
51 const string EXTERNAL_PATH = "file://docs/storage/External";
52 const string NETWORK_PARA = "networkid=";
53 }
54
55 struct FileShareInfo {
56 string providerBundleName_;
57 string targetBundleName_;
58 string providerLowerPath_;
59 string providerSandboxPath_;
60 vector<string> sharePath_;
61 string currentUid_;
62 ShareFileType type_;
63 ino_t stIno_;
64 };
65
66 mutex FileShare::mapMutex_;
67
GetTargetInfo(uint32_t tokenId,string & bundleName,string & currentUid)68 static int32_t GetTargetInfo(uint32_t tokenId, string &bundleName, string ¤tUid)
69 {
70 Security::AccessToken::HapTokenInfo hapInfo;
71 int32_t result = Security::AccessToken::AccessTokenKit::GetHapTokenInfo(tokenId, hapInfo);
72 if (result != 0) {
73 LOGE("Failed to get hap token info %{public}d", result);
74 return result;
75 }
76 bundleName = hapInfo.bundleName;
77 currentUid = to_string(hapInfo.userID);
78
79 int index = hapInfo.instIndex;
80 if (index != 0) {
81 bundleName = to_string(index) + "_" + bundleName;
82 }
83 return 0;
84 }
85
GetProviderInfo(string uriStr,FileShareInfo & info)86 static void GetProviderInfo(string uriStr, FileShareInfo &info)
87 {
88 Uri uri(uriStr);
89 info.providerBundleName_ = uri.GetAuthority();
90 info.providerSandboxPath_ = SandboxHelper::Decode(uri.GetPath());
91 }
92
IsExistDir(const string & path,FileShareInfo & info)93 static bool IsExistDir(const string &path, FileShareInfo &info)
94 {
95 struct stat buf = {};
96 if (stat(path.c_str(), &buf) != 0) {
97 return false;
98 }
99 info.stIno_ = buf.st_ino;
100 return S_ISDIR(buf.st_mode);
101 }
102
IsExistFile(const string & path,FileShareInfo & info)103 static bool IsExistFile(const string &path, FileShareInfo &info)
104 {
105 struct stat buf = {};
106 if (stat(path.c_str(), &buf) != 0) {
107 LOGE("Get path stat failed, err %{public}d", errno);
108 return false;
109 }
110 info.stIno_ = buf.st_ino;
111 return S_ISREG(buf.st_mode);
112 }
113
CheckIfNeedShare(const string & uriStr,ShareFileType type,FileShareInfo & info,const string & path)114 static bool CheckIfNeedShare(const string &uriStr, ShareFileType type, FileShareInfo &info, const string &path)
115 {
116 if (type == ShareFileType::DIR_TYPE) {
117 return true;
118 }
119
120 struct stat buf = {};
121 if (stat(path.c_str(), &buf) != 0) {
122 return true;
123 }
124
125 Uri uri(uriStr);
126 string networkIdInfo = uri.GetQuery();
127 if ((buf.st_nlink != 0 || (!networkIdInfo.empty() && networkIdInfo.find(NETWORK_PARA) == 0)) &&
128 buf.st_ino == info.stIno_) {
129 LOGI("no need create again");
130 return false;
131 }
132
133 return true;
134 }
135
GetSharePath(const string & uri,FileShareInfo & info,uint32_t flag)136 static int32_t GetSharePath(const string &uri, FileShareInfo &info, uint32_t flag)
137 {
138 string shareRPath = DATA_APP_EL2_PATH + info.currentUid_ + SHARE_PATH + info.targetBundleName_ +
139 SHARE_R_PATH + info.providerBundleName_ + info.providerSandboxPath_;
140 string shareRWPath = DATA_APP_EL2_PATH + info.currentUid_ + SHARE_PATH + info.targetBundleName_ +
141 SHARE_RW_PATH + info.providerBundleName_ + info.providerSandboxPath_;
142
143 if (!SandboxHelper::IsValidPath(shareRPath) || !SandboxHelper::IsValidPath(shareRWPath)) {
144 LOGE("Invalid share path");
145 return -EINVAL;
146 }
147
148 if (CheckIfNeedShare(uri, info.type_, info, shareRPath)) {
149 info.sharePath_.push_back(shareRPath);
150 }
151
152 if ((flag & WRITE_URI_PERMISSION) == WRITE_URI_PERMISSION &&
153 CheckIfNeedShare(uri, info.type_, info, shareRWPath)) {
154 info.sharePath_.push_back(shareRWPath);
155 }
156
157 return 0;
158 }
159
GetShareFileType(FileShareInfo & info)160 static int32_t GetShareFileType(FileShareInfo &info)
161 {
162 LOGI("GetShareFileType start");
163 if (!SandboxHelper::CheckValidPath(info.providerLowerPath_)) {
164 LOGE("info.providerLowerPath_ is invalid");
165 return -EINVAL;
166 }
167 if (IsExistFile(info.providerLowerPath_, info)) {
168 info.type_ = ShareFileType::FILE_TYPE;
169 return 0;
170 } else if (IsExistDir(info.providerLowerPath_, info)) {
171 info.type_ = ShareFileType::DIR_TYPE;
172 return 0;
173 }
174 LOGI("GetShareFileType end");
175 return -ENOENT;
176 }
177
GetFileShareInfo(const string & uri,uint32_t tokenId,uint32_t flag,FileShareInfo & info)178 static int32_t GetFileShareInfo(const string &uri, uint32_t tokenId, uint32_t flag, FileShareInfo &info)
179 {
180 int32_t ret = 0;
181 GetProviderInfo(uri, info);
182
183 ret = SandboxHelper::GetPhysicalPath(uri, info.currentUid_, info.providerLowerPath_);
184 if (ret != 0) {
185 LOGE("Failed to get lower path %{public}d", ret);
186 return ret;
187 }
188
189 ret = GetShareFileType(info);
190 if (ret != 0) {
191 LOGE("Failed to get share file type %{public}d", ret);
192 return ret;
193 }
194
195 ret = GetSharePath(uri, info, flag);
196 if (ret != 0) {
197 LOGE("Failed to get share path %{public}d", ret);
198 return ret;
199 }
200 return 0;
201 }
202
MakeDir(const string & path)203 static bool MakeDir(const string &path)
204 {
205 string::size_type index = 0;
206 string subPath;
207 do {
208 index = path.find('/', index + 1);
209 if (index == string::npos) {
210 subPath = path;
211 } else {
212 subPath = path.substr(0, index);
213 }
214
215 if (access(subPath.c_str(), 0) != 0) {
216 if (mkdir(subPath.c_str(), DIR_MODE) != 0) {
217 LOGE("Failed to make dir with %{public}d", errno);
218 return false;
219 }
220 }
221 } while (index != string::npos);
222
223 return true;
224 }
225
DeleteExistShareFile(const string & path)226 static bool DeleteExistShareFile(const string &path)
227 {
228 if (access(path.c_str(), F_OK) == 0) {
229 if (umount2(path.c_str(), MNT_DETACH) != 0 && errno == EBUSY) {
230 LOGE("Umount failed with %{public}d", errno);
231 return false;
232 }
233 if (remove(path.c_str()) != 0 && errno == EBUSY) {
234 LOGE("DeleteExistShareFile, remove failed with %{public}d", errno);
235 return false;
236 }
237 }
238 return true;
239 }
240
DelSharePath(const string & delPath)241 static void DelSharePath(const string &delPath)
242 {
243 if (!SandboxHelper::CheckValidPath(delPath)) {
244 LOGE("DelSharePath, umount path is invalid");
245 return;
246 }
247
248 if (access(delPath.c_str(), F_OK) == 0) {
249 if (umount2(delPath.c_str(), MNT_DETACH) != 0) {
250 LOGE("DelSharePath, umount failed with %{public}d", errno);
251 }
252 if (remove(delPath.c_str()) != 0) {
253 LOGE("DelSharePath, remove failed with %{public}d", errno);
254 }
255 }
256 }
257
UmountDelUris(vector<string> sharePathList,string currentUid,string bundleNameSelf)258 static void UmountDelUris(vector<string> sharePathList, string currentUid, string bundleNameSelf)
259 {
260 string delPathPrefix = DATA_APP_EL2_PATH + currentUid + SHARE_PATH + bundleNameSelf;
261 for (size_t i = 0; i < sharePathList.size(); i++) {
262 Uri uri(sharePathList[i]);
263 string path = SandboxHelper::Decode(uri.GetPath());
264 string bundleName = uri.GetAuthority();
265
266 string delRPath = delPathPrefix + SHARE_R_PATH + bundleName + path;
267 DelSharePath(delRPath);
268
269 string delRWPath = delPathPrefix + SHARE_RW_PATH + bundleName + path;
270 DelSharePath(delRWPath);
271 }
272 }
273
PreparePreShareDir(FileShareInfo & info)274 static int32_t PreparePreShareDir(FileShareInfo &info)
275 {
276 for (size_t i = 0; i < info.sharePath_.size(); i++) {
277 if (access(info.sharePath_[i].c_str(), F_OK) != 0) {
278 string sharePathDir = info.sharePath_[i];
279 size_t posLast = info.sharePath_[i].find_last_of("/");
280 sharePathDir = info.sharePath_[i].substr(0, posLast);
281 if (!MakeDir(sharePathDir.c_str())) {
282 LOGE("Make dir failed with %{public}d", errno);
283 return -errno;
284 }
285 } else {
286 if (!DeleteExistShareFile(info.sharePath_[i])) {
287 return -errno;
288 }
289 }
290 }
291 return 0;
292 }
293
NotRequiredBindMount(const FileShareInfo & info,uint32_t flag,const string & uri)294 static bool NotRequiredBindMount(const FileShareInfo &info, uint32_t flag, const string &uri)
295 {
296 return (info.currentUid_ == "0" ||
297 ((flag & PERSISTABLE_URI_PERMISSION) != 0 && uri.find(EXTERNAL_PATH) != 0) ||
298 uri.find(BROKER_SCHEME_PREFIX) == 0);
299 }
300
StartShareFile(const FileShareInfo & info)301 static int32_t StartShareFile(const FileShareInfo &info)
302 {
303 for (size_t i = 0; i < info.sharePath_.size(); i++) {
304 if (info.type_ == ShareFileType::FILE_TYPE) {
305 int fd = open(info.sharePath_[i].c_str(), O_RDONLY | O_CREAT,
306 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
307 if (fd < 0) {
308 LOGE("Create file failed with %{public}d", errno);
309 return -errno;
310 }
311 close(fd);
312 } else {
313 if (access(info.sharePath_[i].c_str(), 0) != 0 &&
314 mkdir(info.sharePath_[i].c_str(), DIR_MODE) != 0) {
315 LOGE("Failed to make dir with %{public}d", errno);
316 return -errno;
317 }
318 }
319
320 if (mount(info.providerLowerPath_.c_str(), info.sharePath_[i].c_str(),
321 nullptr, MS_BIND, nullptr) != 0) {
322 LOGE("Mount failed with %{public}d", errno);
323 return -errno;
324 }
325 }
326
327 return 0;
328 }
329
CreateSingleShareFile(const string & uri,uint32_t tokenId,uint32_t flag,FileShareInfo & info)330 static int32_t CreateSingleShareFile(const string &uri, uint32_t tokenId, uint32_t flag, FileShareInfo &info)
331 {
332 LOGD("CreateShareFile begin");
333 if (NotRequiredBindMount(info, flag, uri)) {
334 LOGD("Not required to bind mount");
335 return 0;
336 }
337
338 int32_t ret = GetFileShareInfo(uri, tokenId, flag, info);
339 if (ret != 0) {
340 LOGE("Failed to get FileShareInfo with %{public}d", ret);
341 return ret;
342 }
343
344 if (info.sharePath_.size() == 0) {
345 LOGI("no need share path, Create Share File Successfully!");
346 return 0;
347 }
348
349 ret = PreparePreShareDir(info);
350 if (ret != 0) {
351 return ret;
352 }
353 LOGI("StartShareFile start");
354 ret = StartShareFile(info);
355 if (ret != 0) {
356 return ret;
357 }
358
359 info.sharePath_.clear();
360 LOGI("Create Share File Successfully!");
361 return 0;
362 }
363
CreateShareFile(const vector<string> & uriList,uint32_t tokenId,uint32_t flag,vector<int32_t> & retList)364 int32_t FileShare::CreateShareFile(const vector<string> &uriList,
365 uint32_t tokenId,
366 uint32_t flag,
367 vector<int32_t> &retList)
368 {
369 LOGI("CreateShareFile start");
370 lock_guard<mutex> lock(mapMutex_);
371 FileShareInfo info;
372 int32_t ret = GetTargetInfo(tokenId, info.targetBundleName_, info.currentUid_);
373 if (ret != 0) {
374 retList.push_back(ret);
375 LOGE("Failed to get target info %{public}d", ret);
376 return ret;
377 }
378
379 for (const auto &uri : uriList) {
380 int32_t curRet = CreateSingleShareFile(uri, tokenId, flag, info);
381 retList.push_back(curRet);
382 if (curRet != 0) {
383 ret = curRet;
384 LOGE("Create share file failed with %{public}d", errno);
385 }
386 }
387 return ret;
388 }
389
DeleteShareFile(uint32_t tokenId,const vector<string> & uriList)390 int32_t FileShare::DeleteShareFile(uint32_t tokenId, const vector<string> &uriList)
391 {
392 lock_guard<mutex> lock(mapMutex_);
393 string bundleName;
394 string currentUid;
395 int32_t ret = GetTargetInfo(tokenId, bundleName, currentUid);
396 if (ret != 0) {
397 LOGE("Failed to delete share file %{public}d", -EINVAL);
398 return -EINVAL;
399 }
400 UmountDelUris(uriList, currentUid, bundleName);
401
402 LOGI("Delete Share File Successfully!");
403 return 0;
404 }
405 } // namespace AppFileService
406 } // namespace OHOS