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