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 <climits>
19 #include <fcntl.h>
20 #include <pthread.h>
21 #include <sys/ioctl.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.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 "unique_fd.h"
34 #include "uri.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 } //namespace
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, const std::string &deviceId)
179 {
180 int32_t dirFd = open(file, O_RDONLY);
181 if (dirFd < 0) {
182 LOGE("RemoteFileShare::CreateShareFile, open share path failed with %{public}d", errno);
183 return errno;
184 }
185
186 memset_s(shareControl.deviceId, HMDFS_CID_SIZE, '\0', HMDFS_CID_SIZE);
187 if (memcpy_s(shareControl.deviceId, HMDFS_CID_SIZE, deviceId.c_str(), deviceId.size()) != 0) {
188 LOGE("RemoteFileShare::CreateShareFile, memcpy_s failed with %{public}d", errno);
189 close(dirFd);
190 return errno;
191 }
192
193 if (ioctl(dirFd, HMDFS_IOC_SET_SHARE_PATH, &shareControl) < 0) {
194 LOGE("RemoteFileShare::CreateShareFile, ioctl failed with %{public}d", errno);
195 }
196 close(dirFd);
197 return 0;
198 }
199
CheckInputValidity(const int & fd,const int & userId,const std::string & deviceId)200 static int CheckInputValidity(const int &fd, const int &userId, const std::string &deviceId)
201 {
202 return (fd < 0) || (userId < USER_ID_INIT) || (deviceId != SHARE_ALL_DEVICE && deviceId.size() != HMDFS_CID_SIZE);
203 }
204
CreateSharePath(const int & fd,std::string & sharePath,const int & userId,const std::string & deviceId)205 int RemoteFileShare::CreateSharePath(const int &fd,
206 std::string &sharePath,
207 const int &userId,
208 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,
269 const int &userId,
270 std::string &distributedPath,
271 const std::string &bundleName)
272 {
273 distributedPath = DST_PATH_HEAD + std::to_string(userId) + DST_PATH_MID + bundleName +
274 REMOTE_SHARE_PATH_DIR + SandboxHelper::Decode(uri.GetPath());
275 if (distributedPath.size() >= PATH_MAX) {
276 return -EINVAL;
277 }
278
279 return 0;
280 }
281
GetPhysicalPath(Uri & uri,const std::string & userId)282 static std::string GetPhysicalPath(Uri &uri, const std::string &userId)
283 {
284 std::string sandboxPath = SandboxHelper::Decode(uri.GetPath());
285 if (!SandboxHelper::IsValidPath(sandboxPath) || uri.GetScheme() != FILE_SCHEME) {
286 LOGE("Sandbox path from uri is error");
287 return "";
288 }
289
290 std::string physicalPath = "";
291 int ret = SandboxHelper::GetPhysicalPath(uri.ToString(), userId, physicalPath);
292 if (ret != 0) {
293 LOGE("Get physical path failed with %{public}d", ret);
294 return "";
295 }
296 return physicalPath;
297 }
298
InitHmdfsInfo(struct HmdfsDstInfo & hdi,const std::string & physicalPath,const std::string & distributedPath,const std::string & bundleName)299 static void InitHmdfsInfo(struct HmdfsDstInfo &hdi,
300 const std::string &physicalPath,
301 const std::string &distributedPath,
302 const std::string &bundleName)
303 {
304 hdi.localLen = physicalPath.size() + 1;
305 hdi.localPathIndex = reinterpret_cast<uint64_t>(physicalPath.c_str());
306
307 hdi.distributedLen = distributedPath.size() + 1;
308 hdi.distributedPathIndex = reinterpret_cast<uint64_t>(distributedPath.c_str());
309
310 hdi.bundleNameLen = bundleName.size() + 1;
311 hdi.bundleNameIndex = reinterpret_cast<uint64_t>(bundleName.c_str());
312
313 hdi.size = reinterpret_cast<uint64_t>(&hdi.size);
314 }
315
GetLocalNetworkId()316 static std::string GetLocalNetworkId()
317 {
318 const std::string LOCAL = "local";
319 std::string networkId = LOCAL;
320 #ifdef ENABLE_DEVICE_MANAGER
321 auto callback = std::make_shared<InitDMCallback>();
322 int32_t ret = DeviceManager::GetInstance().InitDeviceManager(PACKAGE_NAME, callback);
323 if (ret != 0) {
324 return "";
325 }
326
327 DmDeviceInfo info;
328 ret = DeviceManager::GetInstance().GetLocalDeviceInfo(PACKAGE_NAME, info);
329 networkId = std::string(info.networkId);
330 LOGD("GetLocalNetworkId :%{private}s", networkId.c_str());
331 if (ret != 0 || networkId.empty()) {
332 return "";
333 }
334 #endif
335 return networkId;
336 }
337
SetHmdfsUriInfo(struct HmdfsUriInfo & hui,Uri & uri,uint64_t fileSize,const std::string & networkId,const std::string & bundleName)338 static void SetHmdfsUriInfo(struct HmdfsUriInfo &hui,
339 Uri &uri,
340 uint64_t fileSize,
341 const std::string &networkId,
342 const std::string &bundleName)
343 {
344 hui.uriStr = FILE_SCHEME + "://" + bundleName + DISTRIBUTED_DIR_PATH + REMOTE_SHARE_PATH_DIR +
345 uri.GetPath() + networkId;
346
347 hui.fileSize = fileSize;
348 return;
349 }
350
SetPublicDirHmdfsInfo(const std::string & physicalPath,const std::string & uriStr,struct HmdfsUriInfo & hui,const std::string & networkId)351 static int32_t SetPublicDirHmdfsInfo(const std::string &physicalPath, const std::string &uriStr,
352 struct HmdfsUriInfo &hui, const std::string &networkId)
353 {
354 hui.uriStr = uriStr + networkId;
355 struct stat buf = {};
356 if (stat(physicalPath.c_str(), &buf) != 0) {
357 LOGE("Failed to get physical path stat with %{public}d", -errno);
358 return -errno;
359 }
360 hui.fileSize = static_cast<size_t>(buf.st_size);
361 return 0;
362 }
363
GetMergePathFd(HmdfsDstInfo & hdi,UniqueFd & dirFd,const int32_t & userId)364 static int32_t GetMergePathFd(HmdfsDstInfo &hdi, UniqueFd &dirFd, const int32_t &userId)
365 {
366 LOGI("Open merge path start");
367 std::string ioctlDir = SHAER_PATH_HEAD + std::to_string(userId) + SHAER_PATH_MID;
368 UniqueFd dirMergeFd(open(ioctlDir.c_str(), O_RDONLY));
369 if (dirFd < 0) {
370 LOGE("Open merge path failed with %{public}d", errno);
371 return errno;
372 }
373 int32_t ret = ioctl(dirMergeFd, HMDFS_IOC_GET_DST_PATH, &hdi);
374 if (ret != 0) {
375 LOGE("Ioctl merge failed with %{public}d", errno);
376 return -errno;
377 }
378 dirFd = std::move(dirMergeFd);
379 return 0;
380 }
381
GetDfsUriFromLocal(const std::string & uriStr,const int32_t & userId,struct HmdfsUriInfo & hui)382 int32_t RemoteFileShare::GetDfsUriFromLocal(const std::string &uriStr, const int32_t &userId, struct HmdfsUriInfo &hui)
383 {
384 LOGI("GetDfsUriFromLocal start");
385 Uri uri(uriStr);
386 std::string bundleName = uri.GetAuthority();
387 std::string physicalPath = GetPhysicalPath(uri, std::to_string(userId));
388 if (physicalPath == "") {
389 LOGE("Failed to get physical path");
390 return -EINVAL;
391 }
392 if (bundleName == MEDIA_AUTHORITY) {
393 bundleName = MEDIA_BUNDLE_NAME;
394 }
395
396 std::string networkId = NETWORK_PARA + GetLocalNetworkId();
397 if (bundleName == FILE_MANAGER_AUTHORITY) {
398 (void)SetPublicDirHmdfsInfo(physicalPath, uriStr, hui, networkId);
399 LOGD("GetDfsUriFromLocal successfully");
400 return 0;
401 }
402
403 std::string distributedPath;
404 int ret = GetDistributedPath(uri, userId, distributedPath, bundleName);
405 if (ret != 0) {
406 LOGE("Path is too long with %{public}d", ret);
407 return ret;
408 }
409
410 struct HmdfsDstInfo hdi;
411 InitHmdfsInfo(hdi, physicalPath, distributedPath, bundleName);
412 LOGI("open ioctlDir Create ioctl start");
413 std::string ioctlDir = SHAER_PATH_HEAD + std::to_string(userId) + LOWER_SHARE_PATH_MID;
414 UniqueFd dirFd(open(ioctlDir.c_str(), O_RDONLY));
415 if (dirFd < 0) {
416 LOGE("Open share path failed with %{public}d", errno);
417 return errno;
418 }
419
420 ret = ioctl(dirFd, HMDFS_IOC_GET_DST_PATH, &hdi);
421 if (ret != 0 && GetMergePathFd(hdi, dirFd, userId) != 0) {
422 return errno;
423 }
424 SetHmdfsUriInfo(hui, uri, hdi.size, networkId, bundleName);
425 LOGI("GetDfsUriFromLocal successfully");
426 return 0;
427 }
428
GetDfsUrisFromLocal(const std::vector<std::string> & uriList,const int32_t & userId,std::unordered_map<std::string,HmdfsUriInfo> & uriToDfsUriMaps)429 int32_t RemoteFileShare::GetDfsUrisFromLocal(const std::vector<std::string> &uriList,
430 const int32_t &userId,
431 std::unordered_map<std::string, HmdfsUriInfo> &uriToDfsUriMaps)
432 {
433 LOGI("GetDfsUrisFromLocal start");
434 std::string ioctlDir = SHAER_PATH_HEAD + std::to_string(userId) + LOWER_SHARE_PATH_MID;
435 UniqueFd dirFd(open(ioctlDir.c_str(), O_RDONLY));
436 if (dirFd < 0) {
437 LOGE("Open share path failed with %{public}d", errno);
438 return errno;
439 }
440 LOGI("open ioctlDir end");
441 std::string networkId = NETWORK_PARA + GetLocalNetworkId();
442 for (auto &uriStr : uriList) {
443 Uri uri(uriStr);
444 std::string bundleName = uri.GetAuthority();
445 LOGD("GetDfsUriFromLocal begin, uri: %{private}s", uriStr.c_str());
446 std::string physicalPath = GetPhysicalPath(uri, std::to_string(userId));
447 if (physicalPath == "") {
448 LOGE("Failed to get physical path");
449 return -EINVAL;
450 }
451 if (bundleName == MEDIA_AUTHORITY) {
452 bundleName = MEDIA_BUNDLE_NAME;
453 }
454 if (bundleName == FILE_MANAGER_AUTHORITY) {
455 HmdfsUriInfo dfsUriInfo;
456 (void)SetPublicDirHmdfsInfo(physicalPath, uriStr, dfsUriInfo, networkId);
457 uriToDfsUriMaps.insert({uriStr, dfsUriInfo});
458 LOGD("GetDfsUriFromLocal successfully");
459 continue;
460 }
461
462 std::string distributedPath;
463 int ret = GetDistributedPath(uri, userId, distributedPath, bundleName);
464 if (ret != 0) {
465 LOGE("Path is too long with %{public}d", ret);
466 return ret;
467 }
468 struct HmdfsDstInfo hdi;
469 InitHmdfsInfo(hdi, physicalPath, distributedPath, bundleName);
470 ret = ioctl(dirFd, HMDFS_IOC_GET_DST_PATH, &hdi);
471 if (ret != 0 && GetMergePathFd(hdi, dirFd, userId) != 0) {
472 return errno;
473 }
474 HmdfsUriInfo dfsUriInfo;
475 SetHmdfsUriInfo(dfsUriInfo, uri, hdi.size, networkId, bundleName);
476 uriToDfsUriMaps.insert({uriStr, dfsUriInfo});
477 }
478 LOGI("GetDfsUrisFromLocal successfully");
479 return 0;
480 }
481
TransRemoteUriToLocal(const std::vector<std::string> & uriList,const std::string & networkId,const std::string & deviceId,std::vector<std::string> & resultList)482 int32_t RemoteFileShare::TransRemoteUriToLocal(const std::vector<std::string> &uriList,
483 const std::string &networkId,
484 const std::string &deviceId,
485 std::vector<std::string> &resultList)
486 {
487 if (networkId.empty() || deviceId.empty()) {
488 LOGE("RemoteFileShare::TransRemoteUriToLocal, invalid argument with %{public}d", EINVAL);
489 return EINVAL;
490 }
491 constexpr int splitThree = 3;
492 bool allValid = true;
493 std::vector<std::string> tmpResultList;
494 for (auto &uriStr : uriList) {
495 Uri uri(uriStr);
496 std::string bundleName = uri.GetAuthority();
497 std::string sandboxPath = SandboxHelper::Decode(uri.GetPath());
498 if (!SandboxHelper::IsValidPath(sandboxPath) || uri.GetScheme() != FILE_SCHEME) {
499 LOGE("Sandbox path from uri is error");
500 allValid = false;
501 break;
502 }
503 if ((bundleName != FILE_MANAGER_AUTHORITY) || (sandboxPath.find(FILE_MANAGER_URI_HEAD) != 0)) {
504 LOGE("Sandbox path doesn't begin with docs/storage");
505 allValid = false;
506 break;
507 }
508 int cnt = 0;
509 size_t pos = 0;
510 std::string part;
511 while (cnt < splitThree && pos != std::string::npos) {
512 pos = sandboxPath.find('/', pos + 1);
513 cnt++;
514 }
515 if (pos != std::string::npos) {
516 part = sandboxPath.substr(pos + 1);
517 }
518 if (part.empty()) {
519 allValid = false;
520 break;
521 }
522 std::string localUri = FILE_SCHEME + "://" + bundleName + FILE_MANAGER_URI_HEAD +
523 REMOTE_SHARE_PATH_MID + deviceId + "/" + part;
524 tmpResultList.push_back(localUri);
525 }
526 if (!allValid) {
527 LOGW("Failed to update uriList");
528 resultList = uriList;
529 return -EINVAL;
530 }
531 resultList = tmpResultList;
532 return 0;
533 }
534 } // namespace ModuleRemoteFileShare
535 } // namespace AppFileService
536 } // namespace OHOS
537