1 /*
2 * Copyright (C) 2024 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 #define MLOG_TAG "MediaFuseManager"
17 #include "media_fuse_manager.h"
18
19 #include <fcntl.h>
20 #define FUSE_USE_VERSION 34
21 #include <fuse.h>
22 #include <sys/utsname.h>
23 #include "dfx_const.h"
24 #include "dfx_reporter.h"
25 #include "iservice_registry.h"
26 #include "media_fuse_daemon.h"
27 #include "media_log.h"
28 #include "medialibrary_errno.h"
29 #include "medialibrary_type_const.h"
30 #include "os_account_manager.h"
31 #include "storage_manager_proxy.h"
32 #include "system_ability_definition.h"
33 #include "medialibrary_data_manager.h"
34 #include "media_column.h"
35 #include "media_privacy_manager.h"
36 #include "media_visit_count_manager.h"
37 #include "medialibrary_rdb_utils.h"
38 #include "medialibrary_rdbstore.h"
39 #include "rdb_utils.h"
40 #include "permission_utils.h"
41 #include "abs_permission_handler.h"
42 #include "read_write_permission_handler.h"
43 #include "grant_permission_handler.h"
44 #include "ipc_skeleton.h"
45 #include "permission_used_type.h"
46 #include "medialibrary_object_utils.h"
47 #include "media_file_utils.h"
48 #include "media_app_uri_permission_column.h"
49
50 using namespace std;
51 using namespace OHOS::NativeRdb;
52 using namespace OHOS::RdbDataShareAdapter;
53 using namespace OHOS::Security::AccessToken;
54
55 namespace OHOS {
56 namespace Media {
57 using namespace std;
58
59 const std::string FUSE_ROOT_MEDIA_DIR = "/storage/cloud/files/Photo";
60 const std::string FUSE_OPEN_PHOTO_PRE = "/Photo";
61 const int32_t URI_SLASH_NUM_API9 = 2;
62 const int32_t URI_SLASH_NUM_API10 = 4;
63 const int32_t FUSE_VIRTUAL_ID_DIVIDER = 5;
64 const int32_t FUSE_PHOTO_VIRTUAL_IDENTIFIER = 4;
65 const int32_t BASE_USER_RANGE = 200000;
66 static const map<uint32_t, string> MEDIA_OPEN_MODE_MAP = {
67 { O_RDONLY, MEDIA_FILEMODE_READONLY },
68 { O_WRONLY, MEDIA_FILEMODE_WRITEONLY },
69 { O_RDWR, MEDIA_FILEMODE_READWRITE },
70 { O_WRONLY | O_TRUNC, MEDIA_FILEMODE_WRITETRUNCATE },
71 { O_WRONLY | O_APPEND, MEDIA_FILEMODE_WRITEAPPEND },
72 { O_RDWR | O_TRUNC, MEDIA_FILEMODE_READWRITETRUNCATE },
73 { O_RDWR | O_APPEND, MEDIA_FILEMODE_READWRITEAPPEND },
74 };
75
MediafusePermCheckInfo(const string & filePath,const string & mode,const string & fileId,const string & appId,const int32_t & uid)76 MediafusePermCheckInfo::MediafusePermCheckInfo(const string &filePath, const string &mode, const string &fileId,
77 const string &appId, const int32_t &uid)
78 : filePath_(filePath), mode_(mode), fileId_(fileId), appId_(appId), uid_(uid)
79 {}
80
GetInstance()81 MediaFuseManager &MediaFuseManager::GetInstance()
82 {
83 static MediaFuseManager instance;
84 return instance;
85 }
86
CheckDeviceInLinux()87 bool MediaFuseManager::CheckDeviceInLinux()
88 {
89 struct utsname uts;
90 if (uname(&uts) == -1) {
91 MEDIA_INFO_LOG("uname get failed");
92 return false;
93 }
94 if (strcmp(uts.sysname, "Linux") == 0) {
95 MEDIA_INFO_LOG("uname system is linux");
96 return true;
97 }
98 return false;
99 }
100
Start()101 void MediaFuseManager::Start()
102 {
103 int32_t ret = E_OK;
104 int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
105
106 CHECK_AND_RETURN_INFO_LOG(fuseDaemon_ == nullptr, "Fuse daemon already started");
107
108 /* init current device is in linux or not */
109 isInLinux_ = CheckDeviceInLinux();
110
111 std::string mountpoint;
112 ret = MountFuse(mountpoint);
113 if (ret != E_OK) {
114 DfxReporter::ReportStartResult(DfxType::START_MOUNT_FUSE_FAIL, ret, startTime);
115 MEDIA_ERR_LOG("MountFuse failed");
116 return;
117 }
118
119 MEDIA_INFO_LOG("Mount fuse successfully, mountpoint = %{public}s", mountpoint.c_str());
120 fuseDaemon_ = std::make_shared<MediaFuseDaemon>(mountpoint);
121 ret = fuseDaemon_->StartFuse();
122 if (ret != E_OK) {
123 DfxReporter::ReportStartResult(DfxType::START_FUSE_DAEMON_FAIL, ret, startTime);
124 MEDIA_INFO_LOG("Start fuse daemon failed");
125 UMountFuse();
126 }
127 }
128
Stop()129 void MediaFuseManager::Stop()
130 {
131 MEDIA_INFO_LOG("Stop finished successfully");
132 }
133
countSubString(const string & uri,const string & substr)134 static int32_t countSubString(const string &uri, const string &substr)
135 {
136 int32_t count = 0;
137 size_t start = 0;
138 while ((start = uri.find(substr, start)) != string::npos) {
139 count++;
140 start += substr.length();
141 }
142 return count;
143 }
144
IsFullUri(const string & uri)145 static bool IsFullUri(const string &uri)
146 {
147 bool cond = ((uri.find("/Photo") == 0) && (countSubString(uri, "/") == URI_SLASH_NUM_API10));
148 CHECK_AND_RETURN_RET(!cond, true);
149 cond = (uri.find("/image") == 0) && (countSubString(uri, "/") == URI_SLASH_NUM_API9);
150 CHECK_AND_RETURN_RET(!cond, true);
151 return false;
152 }
153
GetFileIdFromUri(string & fileId,const string & uri)154 static int32_t GetFileIdFromUri(string &fileId, const string &uri)
155 {
156 string tmpPath;
157 uint32_t pos;
158 int32_t virtualId;
159 /* uri = "/Photo/fileid/filename/displayname.jpg" */
160 if (uri.find("/Photo") == 0) {
161 /* tmppath = "fileid/filename/displayname.jpg" */
162 tmpPath = uri.substr(strlen("/Photo/"));
163 /* get fileid end pos */
164 pos = tmpPath.find("/");
165 /* get fileid */
166 fileId = tmpPath.substr(0, pos);
167 } else if (uri.find("/image") == 0) {
168 tmpPath = uri.substr(strlen("/image/"));
169 CHECK_AND_RETURN_RET(!tmpPath.empty(), E_ERR);
170 CHECK_AND_RETURN_RET(all_of(tmpPath.begin(), tmpPath.end(), ::isdigit), E_ERR);
171 CHECK_AND_RETURN_RET_LOG(MediaFileUtils::IsValidInteger(tmpPath), E_ERR, "virtual id invalid");
172 virtualId = stoi(tmpPath);
173 bool cond = ((virtualId + FUSE_PHOTO_VIRTUAL_IDENTIFIER) % FUSE_VIRTUAL_ID_DIVIDER == 0);
174 CHECK_AND_RETURN_RET_LOG(cond, E_ERR, "virtual id err");
175 fileId = to_string((virtualId + FUSE_PHOTO_VIRTUAL_IDENTIFIER) / FUSE_VIRTUAL_ID_DIVIDER);
176 } else {
177 MEDIA_ERR_LOG("uri err");
178 return E_ERR;
179 }
180 return E_SUCCESS;
181 }
182
GetPathFromFileId(string & filePath,const string & fileId)183 static int32_t GetPathFromFileId(string &filePath, const string &fileId)
184 {
185 NativeRdb::RdbPredicates rdbPredicate(PhotoColumn::PHOTOS_TABLE);
186 rdbPredicate.EqualTo(MediaColumn::MEDIA_ID, fileId);
187 rdbPredicate.And()->EqualTo(MediaColumn::MEDIA_DATE_TRASHED, to_string(0));
188 rdbPredicate.And()->EqualTo(MediaColumn::MEDIA_HIDDEN, to_string(0));
189
190 vector<string> columns;
191 columns.push_back(MediaColumn::MEDIA_FILE_PATH);
192 columns.push_back(MediaColumn::MEDIA_DATE_TRASHED);
193 columns.push_back(MediaColumn::MEDIA_HIDDEN);
194 auto resultSet = MediaLibraryRdbStore::Query(rdbPredicate, columns);
195 int32_t numRows = 0;
196 if (resultSet == nullptr) {
197 MEDIA_ERR_LOG("Failed to get rslt");
198 return E_ERR;
199 }
200 int32_t ret = resultSet->GetRowCount(numRows);
201 if ((ret != NativeRdb::E_OK) || (numRows <= 0)) {
202 MEDIA_ERR_LOG("Failed to get filePath");
203 return E_ERR;
204 }
205 if (resultSet->GoToFirstRow() == NativeRdb::E_OK) {
206 filePath = MediaLibraryRdbStore::GetString(resultSet, MediaColumn::MEDIA_FILE_PATH);
207 }
208 return E_SUCCESS;
209 }
210
DoGetAttr(const char * path,struct stat * stbuf)211 int32_t MediaFuseManager::DoGetAttr(const char *path, struct stat *stbuf)
212 {
213 string fileId;
214 string target = path;
215 bool cond;
216 if (isInLinux_) {
217 cond = (path == nullptr || strlen(path) == 0 ||
218 ((target.find("/Photo") != 0) && (target.find("/image") != 0) && (target != "/")));
219 } else {
220 cond = (path == nullptr || strlen(path) == 0 ||
221 ((target.find("/Photo") != 0) && (target.find("/image") != 0)));
222 }
223
224 CHECK_AND_RETURN_RET_LOG(!cond, E_ERR, "Invalid path, %{private}s", path == nullptr ? "null" : path);
225 int32_t ret;
226 if (IsFullUri(target) == false) {
227 ret = lstat(FUSE_ROOT_MEDIA_DIR.c_str(), stbuf);
228 } else {
229 ret = GetFileIdFromUri(fileId, path);
230 CHECK_AND_RETURN_RET_LOG(ret == E_SUCCESS, E_ERR, "get attr fileid fail");
231 ret = GetPathFromFileId(target, fileId);
232 CHECK_AND_RETURN_RET_LOG(ret == E_SUCCESS, E_ERR, "get attr path fail");
233 ret = lstat(target.c_str(), stbuf);
234 }
235 stbuf->st_mode = stbuf->st_mode | 0x6;
236 MEDIA_DEBUG_LOG("get attr succ");
237 return ret;
238 }
239
WrCheckPermission(const string & filePath,const string & mode,const uid_t & uid,AccessTokenID & tokenCaller)240 static int32_t WrCheckPermission(const string &filePath, const string &mode,
241 const uid_t &uid, AccessTokenID &tokenCaller)
242 {
243 vector<string> perms;
244 if (mode.find("r") != string::npos) {
245 perms.push_back(PERM_READ_IMAGEVIDEO);
246 }
247 if (mode.find("w") != string::npos) {
248 perms.push_back(PERM_WRITE_IMAGEVIDEO);
249 }
250 return PermissionUtils::CheckPhotoCallerPermission(perms, uid, tokenCaller)? E_SUCCESS : E_PERMISSION_DENIED;
251 }
252
CheckPermissionType(const vector<int32_t> currentTypes,const set<int32_t> targetTypes)253 static bool CheckPermissionType(const vector<int32_t> currentTypes, const set<int32_t> targetTypes)
254 {
255 for (int32_t type : currentTypes) {
256 if (targetTypes.count(type) > 0) {
257 return true;
258 }
259 }
260 return false;
261 }
262
DbCheckPermission(const string & filePath,const string & mode,const string & fileId,const string & appId,const AccessTokenID & tokenCaller)263 static int32_t DbCheckPermission(const string &filePath, const string &mode, const string &fileId,
264 const string &appId, const AccessTokenID &tokenCaller)
265 {
266 if (appId.empty() || fileId.empty() || (tokenCaller == INVALID_TOKENID)) {
267 MEDIA_ERR_LOG("invalid input");
268 return E_PERMISSION_DENIED;
269 }
270 NativeRdb::RdbPredicates rdbPredicate(TABLE_PERMISSION);
271 rdbPredicate.EqualTo("file_id", fileId);
272 rdbPredicate.And()->BeginWrap()->EqualTo("appid", appId)
273 ->Or()->EqualTo("target_tokenId", to_string(tokenCaller))->EndWrap();
274 vector<string> columns;
275 columns.push_back(FIELD_PERMISSION_TYPE);
276 columns.push_back("file_id");
277 columns.push_back("appid");
278 columns.push_back("target_tokenId");
279 auto resultSet = MediaLibraryRdbStore::Query(rdbPredicate, columns);
280 CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, E_PERMISSION_DENIED, "Failed to get permission type");
281 vector<int32_t> permissionTypes;
282 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
283 int32_t permissionType = MediaLibraryRdbStore::GetInt(resultSet, FIELD_PERMISSION_TYPE);
284 permissionTypes.push_back(permissionType);
285 MEDIA_INFO_LOG("get permissionType %{public}d", permissionType);
286 }
287 bool cond = ((mode.find("r") != string::npos) &&
288 (!CheckPermissionType(permissionTypes, AppUriPermissionColumn::PERMISSION_TYPE_READ)));
289 CHECK_AND_RETURN_RET(!cond, E_PERMISSION_DENIED);
290 cond = ((mode.find("w") != string::npos) &&
291 (!CheckPermissionType(permissionTypes, AppUriPermissionColumn::PERMISSION_TYPE_WRITE)));
292 CHECK_AND_RETURN_RET(!cond, E_PERMISSION_DENIED);
293 return E_SUCCESS;
294 }
295
CheckPermission(uint32_t & tokenCaller)296 int32_t MediafusePermCheckInfo::CheckPermission(uint32_t &tokenCaller)
297 {
298 int err = WrCheckPermission(filePath_, mode_, uid_, tokenCaller);
299 bool rslt;
300 if (err == E_SUCCESS) {
301 MEDIA_INFO_LOG("wr check succ");
302 return true;
303 }
304 err = DbCheckPermission(filePath_, mode_, fileId_, appId_, tokenCaller);
305 if (err == E_SUCCESS) {
306 MEDIA_INFO_LOG("db check succ");
307 rslt = true;
308 } else {
309 rslt = false;
310 }
311 if (mode_.find("r") != string::npos) {
312 PermissionUtils::CollectPermissionInfo(PERM_READ_IMAGEVIDEO, rslt,
313 PermissionUsedTypeValue::PICKER_TYPE, uid_);
314 }
315 if (mode_.find("w") != string::npos) {
316 PermissionUtils::CollectPermissionInfo(PERM_WRITE_IMAGEVIDEO, rslt,
317 PermissionUsedTypeValue::PICKER_TYPE, uid_);
318 }
319 return rslt;
320 }
321
OpenFile(const string & filePath,const string & fileId,const string & mode)322 static int32_t OpenFile(const string &filePath, const string &fileId, const string &mode)
323 {
324 MEDIA_DEBUG_LOG("fuse open file");
325 fuse_context *ctx = fuse_get_context();
326 uid_t uid = ctx->uid;
327 string bundleName;
328 AccessTokenID tokenCaller = INVALID_TOKENID;
329 PermissionUtils::GetClientBundle(uid, bundleName);
330 string appId = PermissionUtils::GetAppIdByBundleName(bundleName, uid);
331 class MediafusePermCheckInfo info(filePath, mode, fileId, appId, uid);
332 int32_t permGranted = info.CheckPermission(tokenCaller);
333 if (permGranted == false) {
334 return E_ERR;
335 }
336 return MediaPrivacyManager(filePath, mode, fileId, appId, bundleName, uid, tokenCaller).Open();
337 }
338
DoOpen(const char * path,int flags,int & fd)339 int32_t MediaFuseManager::DoOpen(const char *path, int flags, int &fd)
340 {
341 uint32_t realFlag = static_cast<uint32_t>(flags) & (O_RDONLY | O_WRONLY | O_RDWR | O_TRUNC | O_APPEND);
342 string fileId;
343 string target;
344 if (MEDIA_OPEN_MODE_MAP.find(realFlag) == MEDIA_OPEN_MODE_MAP.end()) {
345 MEDIA_ERR_LOG("Open mode err, flag = %{public}u", realFlag);
346 return E_ERR;
347 }
348 GetFileIdFromUri(fileId, path);
349 GetPathFromFileId(target, fileId);
350 if (std::string(path).find(FUSE_OPEN_PHOTO_PRE) != std::string::npos) {
351 MEDIA_DEBUG_LOG("MediaFuseManager::DoOpen AddVisitCount fileId[%{public}s]", fileId.c_str());
352 MediaVisitCountManager::AddVisitCount(MediaVisitCountManager::VisitCountType::PHOTO_FS, fileId);
353 }
354 fd = OpenFile(target, fileId, MEDIA_OPEN_MODE_MAP.at(realFlag));
355 if (fd < 0) {
356 MEDIA_ERR_LOG("Open failed, path = %{private}s, errno = %{public}d", target.c_str(), errno);
357 return E_ERR;
358 }
359 return 0;
360 }
361
DoRelease(const char * path,const int & fd)362 int32_t MediaFuseManager::DoRelease(const char *path, const int &fd)
363 {
364 string fileId;
365 string filePath;
366 GetFileIdFromUri(fileId, path);
367 GetPathFromFileId(filePath, fileId);
368 if (fd >= 0) {
369 close(fd);
370 MediaLibraryObjectUtils::ScanFileAsync(filePath, fileId, MediaLibraryApi::API_10);
371 MEDIA_DEBUG_LOG("fuse close file succ");
372 return E_OK;
373 } else {
374 MEDIA_ERR_LOG("fuse close file fail");
375 return E_ERR;
376 }
377 }
378
MountFuse(std::string & mountpoint)379 int32_t MediaFuseManager::MountFuse(std::string &mountpoint)
380 {
381 int devFd = -1;
382 // get user id
383 int32_t userId = static_cast<int32_t>(getuid() / BASE_USER_RANGE);
384
385 // mount fuse
386 auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
387 CHECK_AND_RETURN_RET_LOG(samgr != nullptr, E_FAIL, "Get system ability mgr failed.");
388
389 auto remote = samgr->GetSystemAbility(STORAGE_MANAGER_MANAGER_ID);
390 CHECK_AND_RETURN_RET_LOG(remote != nullptr, E_FAIL, "GetSystemAbility Service Failed.");
391
392 sptr<StorageManager::IStorageManager> proxy_ = iface_cast<StorageManager::IStorageManager>(remote);
393 int32_t err = proxy_->MountMediaFuse(userId, devFd);
394 CHECK_AND_RETURN_RET_LOG(err == E_OK, err, "Mount failed for media fuse daemon, err = %{public}d", err);
395
396 mountpoint = "/dev/fd/" + std::to_string(devFd);
397 return E_OK;
398 }
399
UMountFuse()400 int32_t MediaFuseManager::UMountFuse()
401 {
402 // get user id
403 int32_t userId = static_cast<int32_t>(getuid() / BASE_USER_RANGE);
404
405 // umount fuse
406 auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
407 auto remote = samgr->GetSystemAbility(STORAGE_MANAGER_MANAGER_ID);
408 sptr<StorageManager::IStorageManager> proxy_ = iface_cast<StorageManager::IStorageManager>(remote);
409 int32_t err = proxy_->UMountMediaFuse(userId);
410 CHECK_AND_RETURN_RET_LOG(err == E_OK, err,
411 "UMount failed for media fuse daemon, err = %{public}d", err);
412 return E_OK;
413 }
414 } // namespace Media
415 } // namespace OHOS
416
417