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 &currentUid)
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