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