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
IsValidPath(const std::string & path)156 static bool IsValidPath(const std::string &path)
157 {
158 if (path.find("/./") != std::string::npos ||
159 path.find("/../") != std::string::npos) {
160 return false;
161 }
162 return true;
163 }
164
CreateShareFile(struct HmdfsShareControl & shareControl,const char * file,const std::string & deviceId)165 static int CreateShareFile(struct HmdfsShareControl &shareControl, const char* file,
166 const std::string &deviceId)
167 {
168 int32_t dirFd = open(file, O_RDONLY);
169 if (dirFd < 0) {
170 LOGE("RemoteFileShare::CreateShareFile, open share path failed with %{public}d", errno);
171 return errno;
172 }
173
174 memset_s(shareControl.deviceId, HMDFS_CID_SIZE, '\0', HMDFS_CID_SIZE);
175 if (memcpy_s(shareControl.deviceId, HMDFS_CID_SIZE, deviceId.c_str(), deviceId.size()) != 0) {
176 LOGE("RemoteFileShare::CreateShareFile, memcpy_s failed with %{public}d", errno);
177 close(dirFd);
178 return errno;
179 }
180
181 if (ioctl(dirFd, HMDFS_IOC_SET_SHARE_PATH, &shareControl) < 0) {
182 LOGE("RemoteFileShare::CreateShareFile, ioctl failed with %{public}d", errno);
183 close(dirFd);
184 return errno;
185 }
186 close(dirFd);
187 return 0;
188 }
189
CheckInputValidity(const int & fd,const int & userId,const std::string & deviceId)190 static int CheckInputValidity(const int &fd, const int &userId, const std::string &deviceId)
191 {
192 return (fd < 0) || (userId < USER_ID_INIT) || (deviceId != SHARE_ALL_DEVICE &&
193 deviceId.size() != HMDFS_CID_SIZE);
194 }
195
CreateSharePath(const int & fd,std::string & sharePath,const int & userId,const std::string & deviceId)196 int RemoteFileShare::CreateSharePath(const int &fd, std::string &sharePath,
197 const int &userId, const std::string &deviceId)
198 {
199 struct HmdfsShareControl shareControl;
200 shareControl.fd = fd;
201
202 if (CheckInputValidity(fd, userId, deviceId) != 0) {
203 LOGE("RemoteFileShare::CreateSharePath, invalid argument with %{public}d", EINVAL);
204 return EINVAL;
205 }
206
207 const std::string processName = GetProcessName();
208 if (processName == "") {
209 LOGE("RemoteFileShare::CreateSharePath, GetProcessName failed with %{public}d", errno);
210 return errno;
211 }
212
213 const std::string PACKAGE_PATH = GetLowerSharePath(userId, processName);
214 if (!IsValidPath(PACKAGE_PATH)) {
215 LOGE("RemoteFileShare::CreateSharePath, GetLowerSharePath failed with %{private}s", PACKAGE_PATH.c_str());
216 return EACCES;
217 }
218
219 const std::string LOWER_SHARE_PATH = PACKAGE_PATH + SHARE_PATH_DIR;
220 if (CreateShareDir(PACKAGE_PATH) != 0)
221 return errno;
222 if (CreateShareDir(LOWER_SHARE_PATH) != 0) {
223 DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
224 return errno;
225 }
226
227 const std::string SHARE_PATH = GetSharePath(userId, processName) + SHARE_PATH_DIR;
228 char realPath[PATH_MAX] = {'\0'};
229 if (!realpath(SHARE_PATH.c_str(), realPath)) {
230 LOGE("RemoteFileShare::CreateSharePath, realpath failed with %{public}d", errno);
231 DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
232 return errno;
233 }
234
235 std::string file_name = GetFileName(shareControl.fd);
236 if (file_name == "") {
237 LOGE("RemoteFileShare::CreateSharePath, get error file name");
238 DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
239 return EBADF;
240 }
241 sharePath = SHARE_PATH + "/" + file_name;
242
243 if (CreateShareFile(shareControl, realPath, deviceId) != 0) {
244 LOGE("RemoteFileShare::CreateSharePath, create share file failed with %{public}d", errno);
245 /* When the file is exist, we should not delete the dictionary */
246 if (errno == EEXIST) {
247 return 0;
248 }
249 sharePath = "";
250 DeleteShareDir(PACKAGE_PATH, LOWER_SHARE_PATH);
251 return errno;
252 }
253 LOGI("RemoteFileShare::CreateSharePath, create %{public}s successfully", sharePath.c_str());
254 return 0;
255 }
256
GetDistributedPath(Uri & uri,const int & userId,std::string & distributedPath)257 static int GetDistributedPath(Uri &uri, const int &userId, std::string &distributedPath)
258 {
259 distributedPath = DST_PATH_HEAD + std::to_string(userId) + DST_PATH_MID +
260 uri.GetAuthority() + REMOTE_SHARE_PATH_DIR + uri.GetPath();
261 if (distributedPath.size() >= PATH_MAX) {
262 return -EINVAL;
263 }
264
265 return 0;
266 }
267
GetPhysicalPath(Uri & uri,const std::string & userId)268 static std::string GetPhysicalPath(Uri &uri, const std::string &userId)
269 {
270 std::string sandboxPath = uri.GetPath();
271 if (!IsValidPath(sandboxPath) || uri.GetScheme() != FILE_SCHEME) {
272 LOGE("Sandbox path from uri is error with %{public}s", sandboxPath.c_str());
273 return "";
274 }
275
276 std::string physicalPath = "";
277 int ret = SandboxHelper::GetPhysicalPath(uri.ToString(), userId, physicalPath);
278 if (ret != 0) {
279 LOGE("Get physical path failed with %{public}d", ret);
280 return "";
281 }
282 return physicalPath;
283 }
284
InitHmdfsInfo(struct HmdfsDstInfo & hdi,const std::string & physicalPath,const std::string & distributedPath,const std::string & bundleName)285 static void InitHmdfsInfo(struct HmdfsDstInfo &hdi, const std::string &physicalPath,
286 const std::string &distributedPath, const std::string &bundleName)
287 {
288 hdi.localLen = physicalPath.size() + 1;
289 hdi.localPathIndex = reinterpret_cast<uint64_t>(physicalPath.c_str());
290
291 hdi.distributedLen = distributedPath.size() + 1;
292 hdi.distributedPathIndex = reinterpret_cast<uint64_t>(distributedPath.c_str());
293
294 hdi.bundleNameLen = bundleName.size() + 1;
295 hdi.bundleNameIndex = reinterpret_cast<uint64_t>(bundleName.c_str());
296
297 hdi.size = reinterpret_cast<uint64_t>(&hdi.size);
298 }
299
SetHmdfsUriInfo(struct HmdfsUriInfo & hui,Uri & uri,uint64_t fileSize)300 static void SetHmdfsUriInfo(struct HmdfsUriInfo &hui, Uri &uri, uint64_t fileSize)
301 {
302 std::string bundleName = uri.GetAuthority();
303 std::string path = uri.GetPath();
304
305 hui.uriStr = SandboxHelper::Encode(FILE_SCHEME + "://" + bundleName + DISTRIBUTED_DIR_PATH +
306 REMOTE_SHARE_PATH_DIR + path);
307 hui.fileSize = fileSize;
308 return;
309 }
310
SetPublicDirHmdfsInfo(const std::string & physicalPath,const std::string & uriStr,struct HmdfsUriInfo & hui)311 static int32_t SetPublicDirHmdfsInfo(const std::string &physicalPath, const std::string &uriStr,
312 struct HmdfsUriInfo &hui)
313 {
314 hui.uriStr = uriStr;
315 struct stat buf = {};
316 if (stat(physicalPath.c_str(), &buf) != 0) {
317 LOGE("Failed to get physical path stat with %{public}d", -errno);
318 return -errno;
319 }
320 hui.fileSize = static_cast<size_t>(buf.st_size);
321 return 0;
322 }
323
GetDfsUriFromLocal(const std::string & uriStr,const int32_t & userId,struct HmdfsUriInfo & hui)324 int32_t RemoteFileShare::GetDfsUriFromLocal(const std::string &uriStr, const int32_t &userId,
325 struct HmdfsUriInfo &hui)
326 {
327 Uri uri(SandboxHelper::Decode(uriStr));
328 std::string bundleName = uri.GetAuthority();
329 LOGD("GetDfsUriFromLocal begin with uri:%{private}s, decode uri:%{private}s",
330 uriStr.c_str(), uri.ToString().c_str());
331 std::string physicalPath = GetPhysicalPath(uri, std::to_string(userId));
332 if (physicalPath == "") {
333 LOGE("Failed to get physical path");
334 return -EINVAL;
335 }
336
337 if (bundleName == MEDIA_AUTHORITY || bundleName == FILE_MANAGER_AUTHORITY) {
338 (void)SetPublicDirHmdfsInfo(physicalPath, uriStr, hui);
339 return 0;
340 }
341
342 std::string distributedPath;
343 int ret = GetDistributedPath(uri, userId, distributedPath);
344 if (ret != 0) {
345 LOGE("Path is too long with %{public}d", ret);
346 return ret;
347 }
348
349 struct HmdfsDstInfo hdi;
350 LOGD("PhysicalPath: %{private}s DistributedPath: %{private}s BundleName: %{private}s",
351 physicalPath.c_str(), distributedPath.c_str(), bundleName.c_str());
352 InitHmdfsInfo(hdi, physicalPath, distributedPath, bundleName);
353
354 std::string ioctlDir = SHAER_PATH_HEAD + std::to_string(userId) + SHAER_PATH_MID;
355 int32_t dirFd = open(ioctlDir.c_str(), O_RDONLY);
356 if (dirFd < 0) {
357 LOGE("Open share path failed with %{public}d", errno);
358 return errno;
359 }
360
361 ret = ioctl(dirFd, HMDFS_IOC_GET_DST_PATH, &hdi);
362 if (ret != 0) {
363 LOGE("Ioctl failed with %{public}d", errno);
364 close(dirFd);
365 return -errno;
366 }
367
368 close(dirFd);
369 SetHmdfsUriInfo(hui, uri, hdi.size);
370 LOGD("GetDfsUriFromLocal successfully with %{private}s", hui.uriStr.c_str());
371 return 0;
372 }
373 } // namespace ModuleRemoteFileShare
374 } // namespace AppFileService
375 } // namespace OHOS
376