• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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