• 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 "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