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 #include "sandbox_helper.h"
28 #include "securec.h"
29 #include "uri.h"
30
31 namespace OHOS {
32 namespace AppFileService {
33 namespace ModuleRemoteFileShare {
34 namespace {
35 const int HMDFS_CID_SIZE = 64;
36 const int USER_ID_INIT = 100;
37 const unsigned HMDFS_IOC = 0xf2;
38 const std::string FILE_SCHEME = "file";
39 const std::string DISTRIBUTED_DIR_PATH = "/data/storage/el2/distributedfiles";
40 const std::string DST_PATH_HEAD = "/data/service/el2/";
41 const std::string DST_PATH_MID = "/hmdfs/account/data/";
42 const std::string SHAER_PATH_HEAD = "/mnt/hmdfs/";
43 const std::string SHAER_PATH_MID = "/account/merge_view/services/";
44 const std::string LOWER_SHARE_PATH_HEAD = "/mnt/hmdfs/";
45 const std::string LOWER_SHARE_PATH_MID = "/account/device_view/local/services/";
46 const std::string SHARE_PATH_DIR = "/.share";
47 const std::string REMOTE_SHARE_PATH_DIR = "/.remote_share";
48 const std::string MEDIA_AUTHORITY = "media";
49 const std::string FILE_MANAGER_AUTHORITY = "docs";
50 }
51
52 #define HMDFS_IOC_SET_SHARE_PATH _IOW(HMDFS_IOC, 1, struct HmdfsShareControl)
53 #define HMDFS_IOC_GET_DST_PATH _IOR(HMDFS_IOC, 3, unsigned int)
54
55 struct HmdfsShareControl {
56 int fd;
57 char deviceId[HMDFS_CID_SIZE];
58 };
59
60 struct HmdfsDstInfo {
61 uint64_t localLen;
62 uint64_t localPathIndex;
63 uint64_t distributedLen;
64 uint64_t distributedPathIndex;
65 uint64_t bundleNameLen;
66 uint64_t bundleNameIndex;
67 uint64_t size;
68 };
69
GetProcessName()70 static std::string GetProcessName()
71 {
72 char pthreadName[PATH_MAX];
73 int ret = pthread_getname_np(pthread_self(), pthreadName, sizeof(pthreadName));
74 if (ret != 0) {
75 LOGE("RemoteFileShare::GetProcessName, pthread_getname_np failed with %{public}d", errno);
76 return "";
77 }
78 std::string pthreadNameStr = pthreadName;
79 LOGI("RemoteFileShare::GetProcessName, thread name is %{public}s", pthreadNameStr.c_str());
80 return pthreadNameStr;
81 }
82
GetFileName(const int & fd)83 static std::string GetFileName(const int &fd)
84 {
85 char buf[PATH_MAX] = {'\0'};
86 char filePath[PATH_MAX] = {'\0'};
87
88 int ret = snprintf_s(buf, sizeof(buf), sizeof(buf), "/proc/self/fd/%d", fd);
89 if (ret < 0) {
90 LOGE("RemoteFileShare::GetFileName, snprintf failed with %{public}d", errno);
91 return "";
92 }
93
94 ret = readlink(buf, filePath, PATH_MAX);
95 if (ret < 0 || ret >= PATH_MAX) {
96 LOGE("RemoteFileShare::GetFileName, readlink failed with %{public}d", errno);
97 return "";
98 }
99
100 std::string fileName = filePath;
101 std::size_t firstSlash = fileName.rfind("/");
102 if (firstSlash == fileName.npos) {
103 LOGE("RemoteFileShare::GetFileName, get error path with %{public}s", fileName.c_str());
104 return "";
105 }
106 fileName = fileName.substr(firstSlash + 1, fileName.size() - firstSlash);
107 return fileName;
108 }
109
CreateShareDir(const std::string & path)110 static int CreateShareDir(const std::string &path)
111 {
112 if (access(path.c_str(), F_OK) != 0) {
113 int ret = mkdir(path.c_str(), S_IRWXU);
114 if (ret != 0) {
115 LOGE("RemoteFileShare::CreateShareDir, make dir failed with %{public}d", errno);
116 return errno;
117 }
118 }
119 return 0;
120 }
121
GetSharePath(const int & userId,const std::string & packageName)122 static std::string GetSharePath(const int &userId, const std::string &packageName)
123 {
124 return SHAER_PATH_HEAD + std::to_string(userId) + SHAER_PATH_MID + packageName;
125 }
126
GetLowerSharePath(const int & userId,const std::string & packageName)127 static std::string GetLowerSharePath(const int &userId, const std::string &packageName)
128 {
129 return LOWER_SHARE_PATH_HEAD + std::to_string(userId) + LOWER_SHARE_PATH_MID + packageName;
130 }
131
DeleteShareDir(const std::string & PACKAGE_PATH,const std::string & SHARE_PATH)132 static bool DeleteShareDir(const std::string &PACKAGE_PATH, const std::string &SHARE_PATH)
133 {
134 bool result = true;
135 if (access(SHARE_PATH.c_str(), F_OK) == 0) {
136 int ret = rmdir(SHARE_PATH.c_str());
137 if (ret != 0) {
138 LOGE("RemoteFileShare::DeleteShareDir, delete dir failed with %{public}d", errno);
139 result = false;
140 } else {
141 LOGI("RemoteFileShare::DeleteShareDir, delete %{public}s path successfully", SHARE_PATH.c_str());
142 }
143 }
144 if (access(PACKAGE_PATH.c_str(), F_OK) == 0) {
145 int ret = rmdir(PACKAGE_PATH.c_str());
146 if (ret != 0) {
147 LOGE("RemoteFileShare::DeleteShareDir, delete dir failed with %{public}d", errno);
148 result = false;
149 } else {
150 LOGI("RemoteFileShare::DeleteShareDir, delete %{public}s path successfully", PACKAGE_PATH.c_str());
151 }
152 }
153 return result;
154 }
155
CreateShareFile(struct HmdfsShareControl & shareControl,const char * file,const std::string & deviceId)156 static int CreateShareFile(struct HmdfsShareControl &shareControl, const char* file,
157 const std::string &deviceId)
158 {
159 int32_t dirFd = open(file, O_RDONLY);
160 if (dirFd < 0) {
161 LOGE("RemoteFileShare::CreateShareFile, open share path failed with %{public}d", errno);
162 return errno;
163 }
164
165 memset_s(shareControl.deviceId, HMDFS_CID_SIZE, '\0', HMDFS_CID_SIZE);
166 if (memcpy_s(shareControl.deviceId, HMDFS_CID_SIZE, deviceId.c_str(), deviceId.size()) != 0) {
167 LOGE("RemoteFileShare::CreateShareFile, memcpy_s failed with %{public}d", errno);
168 close(dirFd);
169 return errno;
170 }
171
172 if (ioctl(dirFd, HMDFS_IOC_SET_SHARE_PATH, &shareControl) < 0) {
173 LOGE("RemoteFileShare::CreateShareFile, ioctl failed with %{public}d", errno);
174 close(dirFd);
175 return errno;
176 }
177 close(dirFd);
178 return 0;
179 }
180
CheckInputValidity(const int & fd,const int & userId,const std::string & deviceId)181 static int CheckInputValidity(const int &fd, const int &userId, const std::string &deviceId)
182 {
183 return (fd < 0) || (userId < USER_ID_INIT) || (deviceId != SHARE_ALL_DEVICE &&
184 deviceId.size() != HMDFS_CID_SIZE);
185 }
186
CreateSharePath(const int & fd,std::string & sharePath,const int & userId,const std::string & deviceId)187 int RemoteFileShare::CreateSharePath(const int &fd, std::string &sharePath,
188 const int &userId, const std::string &deviceId)
189 {
190 struct HmdfsShareControl shareControl;
191 shareControl.fd = fd;
192
193 if (CheckInputValidity(fd, userId, deviceId) != 0) {
194 LOGE("RemoteFileShare::CreateSharePath, invalid argument with %{public}d", EINVAL);
195 return EINVAL;
196 }
197
198 const std::string processName = GetProcessName();
199 if (processName == "") {
200 LOGE("RemoteFileShare::CreateSharePath, GetProcessName failed with %{public}d", errno);
201 return errno;
202 }
203
204 const std::string PACKAGE_PATH = GetLowerSharePath(userId, processName);
205 const std::string LOWER_SHARE_PATH = PACKAGE_PATH + SHARE_PATH_DIR;
206 if (CreateShareDir(PACKAGE_PATH) != 0)
207 return errno;
208 if (CreateShareDir(LOWER_SHARE_PATH) != 0) {
209 DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
210 return errno;
211 }
212
213 const std::string SHARE_PATH = GetSharePath(userId, processName) + SHARE_PATH_DIR;
214 char realPath[PATH_MAX] = {'\0'};
215 if (!realpath(SHARE_PATH.c_str(), realPath)) {
216 LOGE("RemoteFileShare::CreateSharePath, realpath failed with %{public}d", errno);
217 DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
218 return errno;
219 }
220
221 std::string file_name = GetFileName(shareControl.fd);
222 if (file_name == "") {
223 LOGE("RemoteFileShare::CreateSharePath, get error file name");
224 DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
225 return EBADF;
226 }
227 sharePath = SHARE_PATH + "/" + file_name;
228
229 if (CreateShareFile(shareControl, realPath, deviceId) != 0) {
230 LOGE("RemoteFileShare::CreateSharePath, create share file failed with %{public}d", errno);
231 /* When the file is exist, we should not delete the dictionary */
232 if (errno == EEXIST) {
233 return 0;
234 }
235 sharePath = "";
236 DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
237 return errno;
238 }
239 LOGI("RemoteFileShare::CreateSharePath, create %{public}s successfully", sharePath.c_str());
240 return 0;
241 }
242
GetDistributedPath(Uri & uri,const int & userId,std::string & distributedPath)243 static int GetDistributedPath(Uri &uri, const int &userId, std::string &distributedPath)
244 {
245 distributedPath = DST_PATH_HEAD + std::to_string(userId) + DST_PATH_MID +
246 uri.GetAuthority() + REMOTE_SHARE_PATH_DIR + uri.GetPath();
247 if (distributedPath.size() >= PATH_MAX) {
248 return -EINVAL;
249 }
250
251 return 0;
252 }
253
IsValidPath(const std::string & path)254 static bool IsValidPath(const std::string &path)
255 {
256 if (path.find("/./") != std::string::npos ||
257 path.find("/../") != std::string::npos) {
258 return false;
259 }
260 return true;
261 }
262
GetPhysicalPath(Uri & uri,const std::string & userId)263 static std::string GetPhysicalPath(Uri &uri, const std::string &userId)
264 {
265 std::string sandboxPath = uri.GetPath();
266 if (!IsValidPath(sandboxPath) || uri.GetScheme() != FILE_SCHEME) {
267 LOGE("Sandbox path from uri is error with %{public}s", sandboxPath.c_str());
268 return "";
269 }
270
271 std::string physicalPath = "";
272 int ret = SandboxHelper::GetPhysicalPath(uri.ToString(), userId, physicalPath);
273 if (ret != 0) {
274 LOGE("Get physical path failed with %{public}d", ret);
275 return "";
276 }
277 return physicalPath;
278 }
279
InitHmdfsInfo(struct HmdfsDstInfo & hdi,const std::string & physicalPath,const std::string & distributedPath,const std::string & bundleName)280 static void InitHmdfsInfo(struct HmdfsDstInfo &hdi, const std::string &physicalPath,
281 const std::string &distributedPath, const std::string &bundleName)
282 {
283 hdi.localLen = physicalPath.size() + 1;
284 hdi.localPathIndex = reinterpret_cast<uint64_t>(physicalPath.c_str());
285
286 hdi.distributedLen = distributedPath.size() + 1;
287 hdi.distributedPathIndex = reinterpret_cast<uint64_t>(distributedPath.c_str());
288
289 hdi.bundleNameLen = bundleName.size() + 1;
290 hdi.bundleNameIndex = reinterpret_cast<uint64_t>(bundleName.c_str());
291
292 hdi.size = reinterpret_cast<uint64_t>(&hdi.size);
293 }
294
SetHmdfsUriInfo(struct HmdfsUriInfo & hui,Uri & uri,uint64_t fileSize)295 static void SetHmdfsUriInfo(struct HmdfsUriInfo &hui, Uri &uri, uint64_t fileSize)
296 {
297 std::string bundleName = uri.GetAuthority();
298 std::string path = uri.GetPath();
299
300 hui.uriStr = SandboxHelper::Encode(FILE_SCHEME + "://" + bundleName + DISTRIBUTED_DIR_PATH +
301 REMOTE_SHARE_PATH_DIR + path);
302 hui.fileSize = fileSize;
303 return;
304 }
305
SetPublicDirHmdfsInfo(const std::string & physicalPath,const std::string & uriStr,struct HmdfsUriInfo & hui)306 static int32_t SetPublicDirHmdfsInfo(const std::string &physicalPath, const std::string &uriStr,
307 struct HmdfsUriInfo &hui)
308 {
309 hui.uriStr = uriStr;
310 struct stat buf = {};
311 if (stat(physicalPath.c_str(), &buf) != 0) {
312 LOGE("Failed to get physical path stat with %{public}d", -errno);
313 return -errno;
314 }
315 hui.fileSize = static_cast<size_t>(buf.st_size);
316 return 0;
317 }
318
GetDfsUriFromLocal(const std::string & uriStr,const int32_t & userId,struct HmdfsUriInfo & hui)319 int32_t RemoteFileShare::GetDfsUriFromLocal(const std::string &uriStr, const int32_t &userId,
320 struct HmdfsUriInfo &hui)
321 {
322 Uri uri(SandboxHelper::Decode(uriStr));
323 std::string bundleName = uri.GetAuthority();
324 LOGD("GetDfsUriFromLocal begin with uri:%{private}s, decode uri:%{private}s",
325 uriStr.c_str(), uri.ToString().c_str());
326 std::string physicalPath = GetPhysicalPath(uri, std::to_string(userId));
327 if (physicalPath == "") {
328 LOGE("Failed to get physical path");
329 return -EINVAL;
330 }
331
332 if (bundleName == MEDIA_AUTHORITY || bundleName == FILE_MANAGER_AUTHORITY) {
333 (void)SetPublicDirHmdfsInfo(physicalPath, uriStr, hui);
334 return 0;
335 }
336
337 std::string distributedPath;
338 int ret = GetDistributedPath(uri, userId, distributedPath);
339 if (ret != 0) {
340 LOGE("Path is too long with %{public}d", ret);
341 return ret;
342 }
343
344 struct HmdfsDstInfo hdi;
345 LOGD("PhysicalPath: %{private}s DistributedPath: %{private}s BundleName: %{private}s",
346 physicalPath.c_str(), distributedPath.c_str(), bundleName.c_str());
347 InitHmdfsInfo(hdi, physicalPath, distributedPath, bundleName);
348
349 std::string ioctlDir = SHAER_PATH_HEAD + std::to_string(userId) + SHAER_PATH_MID;
350 int32_t dirFd = open(ioctlDir.c_str(), O_RDONLY);
351 if (dirFd < 0) {
352 LOGE("Open share path failed with %{public}d", errno);
353 return errno;
354 }
355
356 ret = ioctl(dirFd, HMDFS_IOC_GET_DST_PATH, &hdi);
357 if (ret != 0) {
358 LOGE("Ioctl failed with %{public}d", errno);
359 close(dirFd);
360 return -errno;
361 }
362
363 close(dirFd);
364 SetHmdfsUriInfo(hui, uri, hdi.size);
365 LOGD("GetDfsUriFromLocal successfully with %{private}s", hui.uriStr.c_str());
366 return 0;
367 }
368 } // namespace ModuleRemoteFileShare
369 } // namespace AppFileService
370 } // namespace OHOS
371