1 /*
2  * Copyright (c) 2022-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 "remote_file_share.h"
17 
18 #include <fcntl.h>
19 #include <sys/ioctl.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <climits>
24 #include <pthread.h>
25 
26 #include "log.h"
27 #ifdef ENABLE_DEVICE_MANAGER
28 #include "device_manager.h"
29 #include "device_manager_callback.h"
30 #endif
31 #include "sandbox_helper.h"
32 #include "securec.h"
33 #include "uri.h"
34 #include "unique_fd.h"
35 
36 namespace OHOS {
37 namespace AppFileService {
38 namespace ModuleRemoteFileShare {
39 #ifdef ENABLE_DEVICE_MANAGER
40 using namespace OHOS::DistributedHardware;
41 #endif
42 namespace {
43     const int HMDFS_CID_SIZE = 64;
44     const int USER_ID_INIT = 100;
45     const unsigned HMDFS_IOC = 0xf2;
46     const std::string FILE_SCHEME = "file";
47     const std::string DISTRIBUTED_DIR_PATH = "/data/storage/el2/distributedfiles";
48     const std::string DST_PATH_HEAD = "/data/service/el2/";
49     const std::string DST_PATH_MID = "/hmdfs/account/data/";
50     const std::string SHAER_PATH_HEAD = "/mnt/hmdfs/";
51     const std::string SHAER_PATH_MID = "/account/merge_view/services/";
52     const std::string LOWER_SHARE_PATH_HEAD = "/mnt/hmdfs/";
53     const std::string LOWER_SHARE_PATH_MID = "/account/device_view/local/services/";
54     const std::string SHARE_PATH_DIR = "/.share";
55     const std::string REMOTE_SHARE_PATH_DIR = "/.remote_share";
56     const std::string MEDIA_AUTHORITY = "media";
57     const std::string FILE_MANAGER_AUTHORITY = "docs";
58     const std::string PACKAGE_NAME = "get_dfs_uri_from_local";
59     const std::string NETWORK_PARA = "?networkid=";
60     const std::string MEDIA_BUNDLE_NAME = "com.ohos.medialibrary.medialibrarydata";
61     const std::string FILE_MANAGER_URI_HEAD = "/storage/";
62     const std::string REMOTE_SHARE_PATH_MID = "hmdfs/";
63 }
64 
65 #define HMDFS_IOC_SET_SHARE_PATH    _IOW(HMDFS_IOC, 1, struct HmdfsShareControl)
66 #define HMDFS_IOC_GET_DST_PATH _IOR(HMDFS_IOC, 3, unsigned int)
67 
68 struct HmdfsShareControl {
69     int fd;
70     char deviceId[HMDFS_CID_SIZE];
71 };
72 
73 struct HmdfsDstInfo {
74     uint64_t localLen;
75     uint64_t localPathIndex;
76     uint64_t distributedLen;
77     uint64_t distributedPathIndex;
78     uint64_t bundleNameLen;
79     uint64_t bundleNameIndex;
80     uint64_t size;
81 };
82 
83 #ifdef ENABLE_DEVICE_MANAGER
84 class InitDMCallback : public DmInitCallback {
85 public:
86     InitDMCallback() = default;
87     ~InitDMCallback() override = default;
OnRemoteDied()88     void OnRemoteDied() override {};
89 };
90 #endif
91 
GetProcessName()92 static std::string GetProcessName()
93 {
94     char pthreadName[PATH_MAX];
95     int ret = pthread_getname_np(pthread_self(), pthreadName, sizeof(pthreadName));
96     if (ret != 0) {
97         LOGE("RemoteFileShare::GetProcessName, pthread_getname_np failed with %{public}d", errno);
98         return "";
99     }
100     std::string pthreadNameStr = pthreadName;
101     LOGI("RemoteFileShare::GetProcessName, thread name is %{public}s", pthreadNameStr.c_str());
102     return pthreadNameStr;
103 }
104 
GetFileName(const int & fd)105 static std::string GetFileName(const int &fd)
106 {
107     char buf[PATH_MAX] = {'\0'};
108     char filePath[PATH_MAX] = {'\0'};
109 
110     int ret = snprintf_s(buf, sizeof(buf), sizeof(buf), "/proc/self/fd/%d", fd);
111     if (ret < 0) {
112         LOGE("RemoteFileShare::GetFileName, snprintf failed with %{public}d", errno);
113         return "";
114     }
115 
116     ret = readlink(buf, filePath, PATH_MAX);
117     if (ret < 0 || ret >= PATH_MAX) {
118         LOGE("RemoteFileShare::GetFileName, readlink failed with %{public}d", errno);
119         return "";
120     }
121 
122     std::string fileName = filePath;
123     std::size_t firstSlash = fileName.rfind("/");
124     if (firstSlash == fileName.npos) {
125         LOGE("RemoteFileShare::GetFileName, get error path");
126         return "";
127     }
128     fileName = fileName.substr(firstSlash + 1, fileName.size() - firstSlash);
129     return fileName;
130 }
131 
CreateShareDir(const std::string & path)132 static int CreateShareDir(const std::string &path)
133 {
134     if (access(path.c_str(), F_OK) != 0) {
135         int ret = mkdir(path.c_str(), S_IRWXU);
136         if (ret != 0) {
137             LOGE("RemoteFileShare::CreateShareDir, make dir failed with %{public}d", errno);
138             return errno;
139         }
140     }
141     return 0;
142 }
143 
GetSharePath(const int & userId,const std::string & packageName)144 static std::string GetSharePath(const int &userId, const std::string &packageName)
145 {
146     return SHAER_PATH_HEAD + std::to_string(userId) + SHAER_PATH_MID + packageName;
147 }
148 
GetLowerSharePath(const int & userId,const std::string & packageName)149 static std::string GetLowerSharePath(const int &userId, const std::string &packageName)
150 {
151     return LOWER_SHARE_PATH_HEAD + std::to_string(userId) + LOWER_SHARE_PATH_MID + packageName;
152 }
153 
DeleteShareDir(const std::string & PACKAGE_PATH,const std::string & SHARE_PATH)154 static bool DeleteShareDir(const std::string &PACKAGE_PATH, const std::string &SHARE_PATH)
155 {
156     bool result = true;
157     if (access(SHARE_PATH.c_str(), F_OK) == 0) {
158         int ret = rmdir(SHARE_PATH.c_str());
159         if (ret != 0) {
160             LOGE("RemoteFileShare::DeleteShareDir, delete dir failed with %{public}d", errno);
161             result = false;
162         } else {
163             LOGI("RemoteFileShare::DeleteShareDir, delete path successfully");
164         }
165     }
166     if (access(PACKAGE_PATH.c_str(), F_OK) == 0) {
167         int ret = rmdir(PACKAGE_PATH.c_str());
168         if (ret != 0) {
169             LOGE("RemoteFileShare::DeleteShareDir, delete dir failed with %{public}d", errno);
170             result = false;
171         } else {
172             LOGI("RemoteFileShare::DeleteShareDir, delete path successfully");
173         }
174     }
175     return result;
176 }
177 
CreateShareFile(struct HmdfsShareControl & shareControl,const char * file,const std::string & deviceId)178 static int CreateShareFile(struct HmdfsShareControl &shareControl, const char* file,
179                            const std::string &deviceId)
180 {
181     int32_t dirFd = open(file, O_RDONLY);
182     if (dirFd < 0) {
183         LOGE("RemoteFileShare::CreateShareFile, open share path failed with %{public}d", errno);
184         return errno;
185     }
186 
187     memset_s(shareControl.deviceId, HMDFS_CID_SIZE, '\0', HMDFS_CID_SIZE);
188     if (memcpy_s(shareControl.deviceId, HMDFS_CID_SIZE, deviceId.c_str(), deviceId.size()) != 0) {
189         LOGE("RemoteFileShare::CreateShareFile, memcpy_s failed with %{public}d", errno);
190         close(dirFd);
191         return errno;
192     }
193 
194     if (ioctl(dirFd, HMDFS_IOC_SET_SHARE_PATH, &shareControl) < 0) {
195         LOGE("RemoteFileShare::CreateShareFile, ioctl failed with %{public}d", errno);
196     }
197     close(dirFd);
198     return 0;
199 }
200 
CheckInputValidity(const int & fd,const int & userId,const std::string & deviceId)201 static int CheckInputValidity(const int &fd, const int &userId, const std::string &deviceId)
202 {
203     return (fd < 0) || (userId < USER_ID_INIT) || (deviceId != SHARE_ALL_DEVICE &&
204                                                         deviceId.size() != HMDFS_CID_SIZE);
205 }
206 
CreateSharePath(const int & fd,std::string & sharePath,const int & userId,const std::string & deviceId)207 int RemoteFileShare::CreateSharePath(const int &fd, std::string &sharePath,
208                                      const int &userId, const std::string &deviceId)
209 {
210     struct HmdfsShareControl shareControl;
211     shareControl.fd = fd;
212 
213     if (CheckInputValidity(fd, userId, deviceId) != 0) {
214         LOGE("RemoteFileShare::CreateSharePath, invalid argument with %{public}d", EINVAL);
215         return EINVAL;
216     }
217 
218     const std::string processName = GetProcessName();
219     if (processName == "") {
220         LOGE("RemoteFileShare::CreateSharePath, GetProcessName failed with %{public}d", errno);
221         return errno;
222     }
223 
224     const std::string PACKAGE_PATH = GetLowerSharePath(userId, processName);
225     if (!SandboxHelper::IsValidPath(PACKAGE_PATH)) {
226         LOGE("RemoteFileShare::CreateSharePath, GetLowerSharePath failed");
227         return EACCES;
228     }
229 
230     const std::string LOWER_SHARE_PATH = PACKAGE_PATH + SHARE_PATH_DIR;
231     if (CreateShareDir(PACKAGE_PATH) != 0)
232         return errno;
233     if (CreateShareDir(LOWER_SHARE_PATH) != 0) {
234         DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
235         return errno;
236     }
237 
238     const std::string SHARE_PATH = GetSharePath(userId, processName) + SHARE_PATH_DIR;
239     char realPath[PATH_MAX] = {'\0'};
240     if (!realpath(SHARE_PATH.c_str(), realPath)) {
241         LOGE("RemoteFileShare::CreateSharePath, realpath failed with %{public}d", errno);
242         DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
243         return errno;
244     }
245 
246     std::string file_name = GetFileName(shareControl.fd);
247     if (file_name == "") {
248         LOGE("RemoteFileShare::CreateSharePath, get error file name");
249         DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
250         return EBADF;
251     }
252     sharePath = SHARE_PATH + "/" + file_name;
253 
254     if (CreateShareFile(shareControl, realPath, deviceId) != 0) {
255         LOGE("RemoteFileShare::CreateSharePath, create share file failed with %{public}d", errno);
256         /* When the file is exist, we should not delete the dictionary */
257         if (errno == EEXIST) {
258             return 0;
259         }
260         sharePath = "";
261         DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
262         return errno;
263     }
264     LOGI("RemoteFileShare::CreateSharePath, create successfully");
265     return 0;
266 }
267 
GetDistributedPath(Uri & uri,const int & userId,std::string & distributedPath,const std::string & bundleName)268 static int GetDistributedPath(Uri &uri, const int &userId, std::string &distributedPath, const std::string &bundleName)
269 {
270     distributedPath = DST_PATH_HEAD + std::to_string(userId) + DST_PATH_MID + bundleName +
271                       REMOTE_SHARE_PATH_DIR + SandboxHelper::Decode(uri.GetPath());
272     if (distributedPath.size() >= PATH_MAX) {
273         return -EINVAL;
274     }
275 
276     return 0;
277 }
278 
GetPhysicalPath(Uri & uri,const std::string & userId)279 static std::string GetPhysicalPath(Uri &uri, const std::string &userId)
280 {
281     std::string sandboxPath = SandboxHelper::Decode(uri.GetPath());
282     if (!SandboxHelper::IsValidPath(sandboxPath) || uri.GetScheme() != FILE_SCHEME) {
283         LOGE("Sandbox path from uri is error");
284         return "";
285     }
286 
287     std::string physicalPath = "";
288     int ret = SandboxHelper::GetPhysicalPath(uri.ToString(), userId, physicalPath);
289     if (ret != 0) {
290         LOGE("Get physical path failed with %{public}d", ret);
291         return "";
292     }
293     return physicalPath;
294 }
295 
InitHmdfsInfo(struct HmdfsDstInfo & hdi,const std::string & physicalPath,const std::string & distributedPath,const std::string & bundleName)296 static void InitHmdfsInfo(struct HmdfsDstInfo &hdi, const std::string &physicalPath,
297                           const std::string &distributedPath, const std::string &bundleName)
298 {
299     hdi.localLen = physicalPath.size() + 1;
300     hdi.localPathIndex = reinterpret_cast<uint64_t>(physicalPath.c_str());
301 
302     hdi.distributedLen = distributedPath.size() + 1;
303     hdi.distributedPathIndex = reinterpret_cast<uint64_t>(distributedPath.c_str());
304 
305     hdi.bundleNameLen = bundleName.size() + 1;
306     hdi.bundleNameIndex = reinterpret_cast<uint64_t>(bundleName.c_str());
307 
308     hdi.size = reinterpret_cast<uint64_t>(&hdi.size);
309 }
310 
GetLocalNetworkId()311 static std::string GetLocalNetworkId()
312 {
313     const std::string LOCAL = "local";
314     std::string networkId = LOCAL;
315     #ifdef ENABLE_DEVICE_MANAGER
316     auto callback = std::make_shared<InitDMCallback>();
317     int32_t ret = DeviceManager::GetInstance().InitDeviceManager(PACKAGE_NAME, callback);
318     if (ret != 0) {
319         return "";
320     }
321 
322     DmDeviceInfo info;
323     ret = DeviceManager::GetInstance().GetLocalDeviceInfo(PACKAGE_NAME, info);
324     networkId = std::string(info.networkId);
325     LOGD("GetLocalNetworkId :%{private}s", networkId.c_str());
326     if (ret != 0 || networkId.empty()) {
327         return "";
328     }
329     #endif
330     return networkId;
331 }
332 
SetHmdfsUriInfo(struct HmdfsUriInfo & hui,Uri & uri,uint64_t fileSize,const std::string & networkId,const std::string & bundleName)333 static void SetHmdfsUriInfo(struct HmdfsUriInfo &hui,
334                             Uri &uri,
335                             uint64_t fileSize,
336                             const std::string &networkId,
337                             const std::string &bundleName)
338 {
339     hui.uriStr = FILE_SCHEME + "://" + bundleName + DISTRIBUTED_DIR_PATH + REMOTE_SHARE_PATH_DIR +
340                  uri.GetPath() + networkId;
341 
342     hui.fileSize = fileSize;
343     return;
344 }
345 
SetPublicDirHmdfsInfo(const std::string & physicalPath,const std::string & uriStr,struct HmdfsUriInfo & hui,const std::string & networkId)346 static int32_t SetPublicDirHmdfsInfo(const std::string &physicalPath, const std::string &uriStr,
347                                      struct HmdfsUriInfo &hui, const std::string &networkId)
348 {
349     hui.uriStr = uriStr + networkId;
350     struct stat buf = {};
351     if (stat(physicalPath.c_str(), &buf) != 0) {
352         LOGE("Failed to get physical path stat with %{public}d", -errno);
353         return -errno;
354     }
355     hui.fileSize = static_cast<size_t>(buf.st_size);
356     return 0;
357 }
358 
GetMergePathFd(HmdfsDstInfo & hdi,UniqueFd & dirFd,const int32_t & userId)359 static int32_t GetMergePathFd(HmdfsDstInfo &hdi, UniqueFd &dirFd, const int32_t &userId)
360 {
361     LOGI("Open merge path start");
362     std::string ioctlDir = SHAER_PATH_HEAD + std::to_string(userId) + SHAER_PATH_MID;
363     UniqueFd dirMergeFd(open(ioctlDir.c_str(), O_RDONLY));
364     if (dirFd < 0) {
365         LOGE("Open merge path failed with %{public}d", errno);
366         return errno;
367     }
368     int32_t ret = ioctl(dirMergeFd, HMDFS_IOC_GET_DST_PATH, &hdi);
369     if (ret != 0) {
370         LOGE("Ioctl merge failed with %{public}d", errno);
371         return -errno;
372     }
373     dirFd = std::move(dirMergeFd);
374     return 0;
375 }
376 
GetDfsUriFromLocal(const std::string & uriStr,const int32_t & userId,struct HmdfsUriInfo & hui)377 int32_t RemoteFileShare::GetDfsUriFromLocal(const std::string &uriStr, const int32_t &userId, struct HmdfsUriInfo &hui)
378 {
379     LOGI("GetDfsUriFromLocal start");
380     Uri uri(uriStr);
381     std::string bundleName = uri.GetAuthority();
382     std::string physicalPath = GetPhysicalPath(uri, std::to_string(userId));
383     if (physicalPath == "") {
384         LOGE("Failed to get physical path");
385         return -EINVAL;
386     }
387     if (bundleName == MEDIA_AUTHORITY) {
388         bundleName = MEDIA_BUNDLE_NAME;
389     }
390 
391     std::string networkId = NETWORK_PARA + GetLocalNetworkId();
392     if (bundleName == FILE_MANAGER_AUTHORITY) {
393         (void)SetPublicDirHmdfsInfo(physicalPath, uriStr, hui, networkId);
394         LOGD("GetDfsUriFromLocal successfully");
395         return 0;
396     }
397 
398     std::string distributedPath;
399     int ret = GetDistributedPath(uri, userId, distributedPath, bundleName);
400     if (ret != 0) {
401         LOGE("Path is too long with %{public}d", ret);
402         return ret;
403     }
404 
405     struct HmdfsDstInfo hdi;
406     InitHmdfsInfo(hdi, physicalPath, distributedPath, bundleName);
407     LOGI("open ioctlDir Create ioctl start");
408     std::string ioctlDir = SHAER_PATH_HEAD + std::to_string(userId) + LOWER_SHARE_PATH_MID;
409     UniqueFd dirFd(open(ioctlDir.c_str(), O_RDONLY));
410     if (dirFd < 0) {
411         LOGE("Open share path failed with %{public}d", errno);
412         return errno;
413     }
414 
415     ret = ioctl(dirFd, HMDFS_IOC_GET_DST_PATH, &hdi);
416     if (ret != 0 && GetMergePathFd(hdi, dirFd, userId) != 0) {
417         return errno;
418     }
419     SetHmdfsUriInfo(hui, uri, hdi.size, networkId, bundleName);
420     LOGI("GetDfsUriFromLocal successfully");
421     return 0;
422 }
423 
GetDfsUrisFromLocal(const std::vector<std::string> & uriList,const int32_t & userId,std::unordered_map<std::string,HmdfsUriInfo> & uriToDfsUriMaps)424 int32_t RemoteFileShare::GetDfsUrisFromLocal(const std::vector<std::string> &uriList,
425                                              const int32_t &userId,
426                                              std::unordered_map<std::string, HmdfsUriInfo> &uriToDfsUriMaps)
427 {
428     LOGI("GetDfsUrisFromLocal start");
429     std::string ioctlDir = SHAER_PATH_HEAD + std::to_string(userId) + LOWER_SHARE_PATH_MID;
430     UniqueFd dirFd(open(ioctlDir.c_str(), O_RDONLY));
431     if (dirFd < 0) {
432         LOGE("Open share path failed with %{public}d", errno);
433         return errno;
434     }
435     LOGI("open ioctlDir end");
436     std::string networkId = NETWORK_PARA + GetLocalNetworkId();
437     for (auto &uriStr : uriList) {
438         Uri uri(uriStr);
439         std::string bundleName = uri.GetAuthority();
440         LOGD("GetDfsUriFromLocal begin");
441         std::string physicalPath = GetPhysicalPath(uri, std::to_string(userId));
442         if (physicalPath == "") {
443             LOGE("Failed to get physical path");
444             return -EINVAL;
445         }
446         if (bundleName == MEDIA_AUTHORITY) {
447             bundleName = MEDIA_BUNDLE_NAME;
448         }
449 
450         if (bundleName == FILE_MANAGER_AUTHORITY) {
451             HmdfsUriInfo dfsUriInfo;
452             (void)SetPublicDirHmdfsInfo(physicalPath, uriStr, dfsUriInfo, networkId);
453             uriToDfsUriMaps.insert({uriStr, dfsUriInfo});
454             LOGD("GetDfsUriFromLocal successfully");
455             continue;
456         }
457 
458         std::string distributedPath;
459         int ret = GetDistributedPath(uri, userId, distributedPath, bundleName);
460         if (ret != 0) {
461             LOGE("Path is too long with %{public}d", ret);
462             return ret;
463         }
464         struct HmdfsDstInfo hdi;
465         InitHmdfsInfo(hdi, physicalPath, distributedPath, bundleName);
466         ret = ioctl(dirFd, HMDFS_IOC_GET_DST_PATH, &hdi);
467         if (ret != 0 && GetMergePathFd(hdi, dirFd, userId) != 0) {
468             return errno;
469         }
470         HmdfsUriInfo dfsUriInfo;
471         SetHmdfsUriInfo(dfsUriInfo, uri, hdi.size, networkId, bundleName);
472         uriToDfsUriMaps.insert({uriStr, dfsUriInfo});
473     }
474     LOGI("GetDfsUrisFromLocal successfully");
475     return 0;
476 }
477 
TransRemoteUriToLocal(const std::vector<std::string> & uriList,const std::string & networkId,const std::string & deviceId,std::vector<std::string> & resultList)478 int32_t RemoteFileShare::TransRemoteUriToLocal(const std::vector<std::string> &uriList,
479                                                const std::string &networkId,
480                                                const std::string &deviceId,
481                                                std::vector<std::string> &resultList)
482 {
483     if (networkId.empty() || deviceId.empty()) {
484         LOGE("RemoteFileShare::TransRemoteUriToLocal, invalid argument with %{public}d", EINVAL);
485         return EINVAL;
486     }
487     constexpr int splitThree = 3;
488     bool allValid = true;
489     std::vector<std::string> tmpResultList;
490     for (auto &uriStr : uriList) {
491         Uri uri(uriStr);
492         std::string bundleName = uri.GetAuthority();
493         std::string sandboxPath = SandboxHelper::Decode(uri.GetPath());
494         if (!SandboxHelper::IsValidPath(sandboxPath) || uri.GetScheme() != FILE_SCHEME) {
495             LOGE("Sandbox path from uri is error");
496             allValid = false;
497             break;
498         }
499         if ((bundleName != FILE_MANAGER_AUTHORITY) || (sandboxPath.find(FILE_MANAGER_URI_HEAD) != 0)) {
500             LOGE("Sandbox path doesn't begin with docs/storage");
501             allValid = false;
502             break;
503         }
504         int cnt = 0;
505         size_t pos = 0;
506         std::string part;
507         while (cnt < splitThree && pos != std::string::npos) {
508             pos = sandboxPath.find('/', pos + 1);
509             cnt++;
510         }
511         if (pos != std::string::npos) {
512             part = sandboxPath.substr(pos + 1);
513         }
514         if (part.empty()) {
515             allValid = false;
516             break;
517         }
518         std::string localUri = FILE_SCHEME + "://" + bundleName + FILE_MANAGER_URI_HEAD +
519                                REMOTE_SHARE_PATH_MID + deviceId + "/" + part;
520         tmpResultList.push_back(localUri);
521     }
522     if (!allValid) {
523         LOGW("Failed to update uriList");
524         resultList = uriList;
525         return -EINVAL;
526     }
527     resultList = tmpResultList;
528     return 0;
529 }
530 } // namespace ModuleRemoteFileShare
531 } // namespace AppFileService
532 } // namespace OHOS
533