/*
 * Copyright (C) 2024-2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define MLOG_TAG "MediaFuseManager"
#include "media_fuse_manager.h"

#include <fcntl.h>
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
#include <fuse.h>
#include <sys/utsname.h>
#include "dfx_const.h"
#include "dfx_manager.h"
#include "dfx_reporter.h"
#include "iservice_registry.h"
#include "media_cloud_permission_check.h"
#include "media_fuse_daemon.h"
#include "media_fuse_hdc_operations.h"
#include "media_log.h"
#include "medialibrary_errno.h"
#include "medialibrary_type_const.h"
#include "medialibrary_db_const.h"
#include "os_account_manager.h"
#include "storage_manager_proxy.h"
#include "safe_map.h"
#include "system_ability_definition.h"
#include "settings_data_manager.h"
#include "medialibrary_data_manager.h"
#include "media_column.h"
#include "media_privacy_manager.h"
#include "media_permission_check.h"
#include "media_visit_count_manager.h"
#include "media_edit_utils.h"
#include "medialibrary_rdb_utils.h"
#include "medialibrary_rdbstore.h"
#include "rdb_utils.h"
#include "permission_utils.h"
#include "abs_permission_handler.h"
#include "read_write_permission_handler.h"
#include "grant_permission_handler.h"
#include "heif_transcoding_check_utils.h"
#include "ipc_skeleton.h"
#include "medialibrary_object_utils.h"
#include "media_file_utils.h"
#include "media_app_uri_permission_column.h"
#include "medialibrary_ptp_operations.h"
#include "medialibrary_photo_operations.h"
#include "result_set_utils.h"
#include "medialibrary_transcode_data_aging_operation.h"
#ifdef MEDIALIBRARY_LAKE_SUPPORT
#include "file_const.h"
#endif
#include "medialibrary_bundle_manager.h"
#include "transcode_compatible_info_operations.h"
#include "tokenid_kit.h"

using namespace std;
using namespace OHOS::NativeRdb;
using namespace OHOS::RdbDataShareAdapter;
using namespace OHOS::Security::AccessToken;

namespace OHOS {
namespace Media {
using namespace std;

const std::string FUSE_ROOT_MEDIA_DIR = "/storage/cloud/files/Photo";
const std::string FUSE_OPEN_PHOTO_PRE = "/Photo";
const int32_t URI_SLASH_NUM_API9 = 1;
const int32_t URI_SLASH_NUM_API10 = 3;
const int32_t FUSE_VIRTUAL_ID_DIVIDER = 5;
const int32_t FUSE_PHOTO_VIRTUAL_IDENTIFIER = 4;
const int32_t BASE_USER_RANGE = 200000;
const int32_t FILE_FAIL = -2;
const int32_t PHOTO_POSITION_TYPE_CLOUD = 2;
const int32_t PERMISSION_BUNDLENAME_EMPTY = 1;
static constexpr int64_t MILLISECONDS_THRESHOLD = 1000000000000LL;
static constexpr int64_t MILLISECONDS_PER_SECOND = 1000LL;
static constexpr int32_t HDC_FIRST_ARGS = 0;
static constexpr int32_t HDC_SECOND_ARGS = 1;
static constexpr int32_t HDC_THIRD_ARGS = 2;

struct CloudAssetTimeQueryParams {
    string fileId;
    int32_t position;
    int64_t accesstime;
    int64_t changeTime;
    explicit CloudAssetTimeQueryParams(
        const string& fileId,
        int32_t position = 0,
        int64_t accesstime = 0,
        int64_t changeTime = 0
    ) : fileId(fileId), position(position), accesstime(accesstime), changeTime(changeTime) {}
};

struct PathValidationParams {
    string fileId;
    string storageName;
    string displayName;
    int32_t fileSourceType;
    int32_t ownerAlbumId;

    explicit PathValidationParams(
        const string& fileId,
        string storageName = "",
        string displayName = "",
        int32_t fileSourceType = FileSourceType::MEDIA,
        int32_t ownerAlbumId = 0
    ) : fileId(fileId), storageName(storageName), displayName(displayName), fileSourceType(fileSourceType),
        ownerAlbumId(ownerAlbumId) {}
};

static const map<uint32_t, string> MEDIA_OPEN_MODE_MAP = {
    { O_RDONLY, MEDIA_FILEMODE_READONLY },
    { O_WRONLY, MEDIA_FILEMODE_WRITEONLY },
    { O_RDWR, MEDIA_FILEMODE_READWRITE },
    { O_WRONLY | O_TRUNC, MEDIA_FILEMODE_WRITETRUNCATE },
    { O_WRONLY | O_APPEND, MEDIA_FILEMODE_WRITEAPPEND },
    { O_RDWR | O_TRUNC, MEDIA_FILEMODE_READWRITETRUNCATE },
    { O_RDWR | O_APPEND, MEDIA_FILEMODE_READWRITEAPPEND },
};
SafeMap<int, time_t> MEDIA_OPEN_WRITE_MAP;
SafeMap<std::string, bool> MEDIA_CREATE_WRITE_MAP;

static bool IsCriticalPhoto(const string &fileId)
{
    auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
    if (rdbStore == nullptr) {
        MEDIA_ERR_LOG("Failed to get RDB store");
        return false;
    }

    vector<string> columns = { PhotoColumn::PHOTO_IS_CRITICAL };
    AbsRdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
    predicates.EqualTo(MediaColumn::MEDIA_ID, fileId);

    auto resultSet = rdbStore->Query(predicates, columns);
    if (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK) {
        return false;
    }

    int32_t isCritical = 0;
    int32_t columnIndex = 0;
    resultSet->GetColumnIndex(PhotoColumn::PHOTO_IS_CRITICAL, columnIndex);
    resultSet->GetInt(columnIndex, isCritical);

    return isCritical == 1;
}

static int32_t CheckCriticalPhotoPermission(const string &fileId, const uid_t &uid)
{
    if (!IsCriticalPhoto(fileId)) {
        return E_SUCCESS;
    }

    if (!PermissionUtils::CheckCallerPermission(MANAGE_RISK_PHOTOS)) {
        MEDIA_ERR_LOG("Permission denied: MANAGE_RISK_PHOTOS required for critical photo access");
        return E_PERMISSION_DENIED;
    }

    return E_SUCCESS;
}

MediafusePermCheckInfo::MediafusePermCheckInfo(const string &filePath, const string &mode, const string &fileId,
    const string &appId, const int32_t &uid)
    : filePath_(filePath), mode_(mode), fileId_(fileId), appId_(appId), uid_(uid)
{}

MediaFuseManager &MediaFuseManager::GetInstance()
{
    static MediaFuseManager instance;
    return instance;
}

bool MediaFuseManager::CheckDeviceInLinux()
{
    struct utsname uts;
    if (uname(&uts) == -1) {
        MEDIA_INFO_LOG("uname get failed");
        return false;
    }
    if (strcmp(uts.sysname, "Linux") == 0) {
        MEDIA_INFO_LOG("uname system is linux");
        return true;
    }
    return false;
}

void MediaFuseManager::Start()
{
    int32_t ret = E_OK;
    int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();

    UMountFuse();

    CHECK_AND_RETURN_INFO_LOG(fuseDaemon_ == nullptr, "Fuse daemon already started");

    /* init current device is in linux or not */
    isInLinux_ = CheckDeviceInLinux();

    std::string mountpoint;
    ret = MountFuse(mountpoint);
    if (ret != E_OK) {
        DfxReporter::ReportStartResult(DfxType::START_MOUNT_FUSE_FAIL, ret, startTime);
        MEDIA_ERR_LOG("MountFuse failed");
        return;
    }

    MEDIA_INFO_LOG("Mount fuse successfully, mountpoint = %{public}s", mountpoint.c_str());
    fuseDaemon_ = std::make_shared<MediaFuseDaemon>(mountpoint);
    CHECK_AND_RETURN_LOG(fuseDaemon_ != nullptr, "Create fuse daemon failed");
    ret = fuseDaemon_->StartFuse();
    if (ret != E_OK) {
        DfxReporter::ReportStartResult(DfxType::START_FUSE_DAEMON_FAIL, ret, startTime);
        MEDIA_INFO_LOG("Start fuse daemon failed");
        UMountFuse();
    }
}

void MediaFuseManager::Stop()
{
    UMountFuse();
    fuseDaemon_ = nullptr;
    MEDIA_INFO_LOG("Stop finished successfully");
}

static int32_t countSubString(const string &uri, const string &substr)
{
    int32_t count = 0;
    size_t start = 0;
    while ((start = uri.find(substr, start)) != string::npos) {
        count++;
        start += substr.length();
    }
    return count;
}

static string GetStorageNameFromUri(const string &uri)
{
    if (uri.empty()) {
        return uri;
    }
    string tmpPath;
    auto index = uri.rfind("/");
    if (index != string::npos) {
        string uriWithoutDisplayname = uri.substr(0, index);
        tmpPath = MediaFileUtils::SplitByChar(uriWithoutDisplayname, '/');
    }
    return tmpPath;
}

static string GetStorageNameFromFilePath(const string &filePath)
{
    string realDisplayName = MediaFileUtils::GetFileName(filePath);
    if (realDisplayName.empty()) {
        return realDisplayName;
    }
    return MediaFileUtils::GetTitleFromDisplayName(realDisplayName);
}

static string GetStorageDirectoryFromStoragePath(const string &storagePath)
{
    if (storagePath.empty()) {
        return storagePath;
    }
    string storageDirectory = "";
    auto index = storagePath.rfind("/");
    if (index != string::npos) {
        storageDirectory = storagePath.substr(0, index);
    }
    return storageDirectory;
}

static int32_t GetFileIdFromUri(string &fileId, const string &uri)
{
    int32_t splitCount = countSubString(uri, "/");
    string tmpPath = uri.substr(strlen("/"));
    if (splitCount == URI_SLASH_NUM_API9) {
        CHECK_AND_RETURN_RET(!tmpPath.empty(), E_ERR);
        CHECK_AND_RETURN_RET(all_of(tmpPath.begin(), tmpPath.end(), ::isdigit), E_ERR);
        CHECK_AND_RETURN_RET_LOG(MediaFileUtils::IsValidInteger(tmpPath), E_ERR, "virtual id invalid");
        int32_t virtualId = stoi(tmpPath);
        bool cond = ((virtualId + FUSE_PHOTO_VIRTUAL_IDENTIFIER) % FUSE_VIRTUAL_ID_DIVIDER == 0);
        CHECK_AND_RETURN_RET_LOG(cond, E_ERR, "virtual id err");
        fileId = to_string((virtualId + FUSE_PHOTO_VIRTUAL_IDENTIFIER) / FUSE_VIRTUAL_ID_DIVIDER);
    } else if (splitCount == URI_SLASH_NUM_API10) {
        uint32_t pos = tmpPath.find("/");
        fileId = tmpPath.substr(0, pos);
    } else {
        MEDIA_ERR_LOG("uri err");
        return E_ERR;
    }
    return E_SUCCESS;
}

static int32_t GetMessageFromUriForGetAttr(string &fileId, string &storageName, string &displayName,
    const string &uri)
{
    string tmpPath;
    if (uri.find("/") == 0) {
        tmpPath = uri.substr(strlen("/"));
        CHECK_AND_RETURN_RET(!tmpPath.empty(), E_ERR);
        size_t pos = tmpPath.find("/");
        if (pos < tmpPath.size()) {
            tmpPath = tmpPath.substr(0, pos);
        }
        CHECK_AND_RETURN_RET(all_of(tmpPath.begin(), tmpPath.end(), ::isdigit), E_ERR);
        CHECK_AND_RETURN_RET_LOG(MediaFileUtils::IsValidInteger(tmpPath), E_ERR, "virtual id invalid");
        fileId = tmpPath;
        displayName = MediaFileUtils::GetFileName(uri);
        CHECK_AND_RETURN_RET_LOG(!MediaFileUtils::GetExtensionFromPath(displayName).empty(), E_ERR,
            "virtual displayName invalid");
        storageName = GetStorageNameFromUri(uri);
        CHECK_AND_RETURN_RET_LOG(!storageName.empty(), E_ERR, "virtual storageName invalid");
        int32_t splitCount = countSubString(uri, "/");
        if (splitCount == URI_SLASH_NUM_API9) {
            int32_t virtualId = stoi(tmpPath);
            bool cond = ((virtualId + FUSE_PHOTO_VIRTUAL_IDENTIFIER) % FUSE_VIRTUAL_ID_DIVIDER == 0);
            CHECK_AND_RETURN_RET_LOG(cond, E_ERR, "virtual id err");
            fileId = to_string((virtualId + FUSE_PHOTO_VIRTUAL_IDENTIFIER) / FUSE_VIRTUAL_ID_DIVIDER);
        }
    } else {
        MEDIA_ERR_LOG("uri err");
        return E_ERR;
    }
    return E_SUCCESS;
}

static int32_t GetPathFromFileId(string &filePath, const string &fileId)
{
    NativeRdb::RdbPredicates rdbPredicate(PhotoColumn::PHOTOS_TABLE);
    rdbPredicate.EqualTo(MediaColumn::MEDIA_ID, fileId);
    rdbPredicate.And()->EqualTo(MediaColumn::MEDIA_DATE_TRASHED, to_string(0));
    rdbPredicate.And()->EqualTo(MediaColumn::MEDIA_HIDDEN, to_string(0));

    vector<string> columns;
    columns.push_back(MediaColumn::MEDIA_FILE_PATH);
    columns.push_back(MediaColumn::MEDIA_DATE_TRASHED);
    columns.push_back(MediaColumn::MEDIA_HIDDEN);
    columns.push_back(PhotoColumn::PHOTO_STORAGE_PATH);
    columns.push_back(PhotoColumn::PHOTO_FILE_SOURCE_TYPE);
    auto resultSet = MediaLibraryRdbStore::Query(rdbPredicate, columns);
    int32_t numRows = 0;
    if (resultSet == nullptr) {
        MEDIA_ERR_LOG("Failed to get rslt");
        return E_ERR;
    }
    int32_t ret = resultSet->GetRowCount(numRows);
    if ((ret != NativeRdb::E_OK) || (numRows <= 0)) {
        MEDIA_ERR_LOG("Failed to get filePath");
        return E_ERR;
    }
    if (resultSet->GoToFirstRow() == NativeRdb::E_OK) {
#ifdef MEDIALIBRARY_LAKE_SUPPORT
        int32_t sourceType = GetInt32Val(PhotoColumn::PHOTO_FILE_SOURCE_TYPE, resultSet);
        filePath = (sourceType == FileSourceType::MEDIA_HO_LAKE || sourceType == FileSourceType::FILE_MANAGER) ?
            GetStringVal(PhotoColumn::PHOTO_STORAGE_PATH, resultSet) :
            GetStringVal(MediaColumn::MEDIA_FILE_PATH, resultSet);
#else
        filePath = GetStringVal(MediaColumn::MEDIA_FILE_PATH, resultSet);
#endif
    }
    return E_SUCCESS;
}

static int32_t GetPathFromFileIdForGetAttr(string &filePath, const string &fileId,
    CloudAssetTimeQueryParams &cloudAssetTimeQueryParams, PathValidationParams &pathValidationParams)
{
    NativeRdb::RdbPredicates rdbPredicate(PhotoColumn::PHOTOS_TABLE);
    rdbPredicate.EqualTo(MediaColumn::MEDIA_ID, fileId);
    rdbPredicate.And()->EqualTo(MediaColumn::MEDIA_DATE_TRASHED, to_string(0));
    rdbPredicate.And()->EqualTo(MediaColumn::MEDIA_HIDDEN, to_string(0));

    vector<string> columns;
    columns.push_back(MediaColumn::MEDIA_FILE_PATH);
    columns.push_back(MediaColumn::MEDIA_DATE_TRASHED);
    columns.push_back(MediaColumn::MEDIA_HIDDEN);
    columns.push_back(PhotoColumn::PHOTO_STORAGE_PATH);
    columns.push_back(PhotoColumn::PHOTO_FILE_SOURCE_TYPE);
    columns.push_back(PhotoColumn::PHOTO_POSITION);
    columns.push_back(PhotoColumn::PHOTO_LAST_VISIT_TIME);
    columns.push_back(MediaColumn::MEDIA_DATE_MODIFIED);
    columns.push_back(PhotoColumn::PHOTO_OWNER_ALBUM_ID);
    columns.push_back(MediaColumn::MEDIA_NAME);
    auto resultSet = MediaLibraryRdbStore::Query(rdbPredicate, columns);
    int32_t numRows = 0;
    CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, E_ERR, "Failed to get rslt");
    int32_t ret = resultSet->GetRowCount(numRows);
    bool cond = ((ret != NativeRdb::E_OK) || (numRows <= 0));
    CHECK_AND_RETURN_RET_LOG(!cond, E_ERR, "Failed to get filePath");

    if (resultSet->GoToFirstRow() == NativeRdb::E_OK) {
        int32_t sourceType = GetInt32Val(PhotoColumn::PHOTO_FILE_SOURCE_TYPE, resultSet);
        filePath = (sourceType == FileSourceType::MEDIA_HO_LAKE || sourceType == FileSourceType::FILE_MANAGER) ?
            GetStringVal(PhotoColumn::PHOTO_STORAGE_PATH, resultSet) :
            GetStringVal(MediaColumn::MEDIA_FILE_PATH, resultSet);
        cloudAssetTimeQueryParams.position = GetInt32Val(PhotoColumn::PHOTO_POSITION, resultSet);
        cloudAssetTimeQueryParams.accesstime = GetInt64Val(PhotoColumn::PHOTO_LAST_VISIT_TIME, resultSet);
        cloudAssetTimeQueryParams.changeTime = GetInt64Val(MediaColumn::MEDIA_DATE_MODIFIED, resultSet);
        pathValidationParams.storageName =
            GetStorageNameFromFilePath(GetStringVal(MediaColumn::MEDIA_FILE_PATH, resultSet));
        pathValidationParams.displayName = GetStringVal(MediaColumn::MEDIA_NAME, resultSet);
        pathValidationParams.fileSourceType = sourceType;
        pathValidationParams.ownerAlbumId = GetInt32Val(PhotoColumn::PHOTO_OWNER_ALBUM_ID, resultSet);
    }
    resultSet->Close();
    return E_SUCCESS;
}

static int32_t CheckMediaLibraryPathValidity(string &filePath, string &fileId, string storageName,
    PathValidationParams &params, const string &displayName)
{
    if (params.fileSourceType != static_cast<int32_t>(FileSourceType::FILE_MANAGER)) {
        return E_SUCCESS;
    }

    string targetStorageName = params.storageName;
    string targetDisplayName = params.displayName;
    CHECK_AND_RETURN_RET_LOG(!targetStorageName.empty() && storageName == targetStorageName, E_ERR,
        "check filemanager asset storageName fail");
    CHECK_AND_RETURN_RET_LOG(!targetDisplayName.empty() && displayName == targetDisplayName, E_ERR,
        "check filemanager asset displayName fail");

    CHECK_AND_RETURN_RET_LOG(params.ownerAlbumId > 0 && !displayName.empty(), E_ERR, "invalid filemanager asset");
    
    vector<string> fileNames;
    string storageDirectory = GetStorageDirectoryFromStoragePath(filePath);
    MediaFileUtils::GetAllFileNameListUnderPath(storageDirectory, fileNames);

    int32_t sameNameCount = 0;
    for (const auto &name : fileNames) {
        if (name == displayName) {
            sameNameCount++;
        }
    }
    CHECK_AND_RETURN_RET_LOG(sameNameCount == 1, E_ERR,
        "file manager directoryhas duplicate names, count: %{public}d", sameNameCount);

    return E_SUCCESS;
}

int32_t MediaFuseManager::DoMedialibraryReadPermission(const string &fileId, const string &target, uid_t uid)
{
    string bundleName;
    AccessTokenID tokenCaller = INVALID_TOKENID;
    int32_t permGranted = E_PERMISSION_DENIED;
    PermissionUtils::GetClientBundle(uid, bundleName);
    if (bundleName.empty()) {
        MEDIA_DEBUG_LOG("Get bundleName is empty for uid %{public}d", uid);
        return PERMISSION_BUNDLENAME_EMPTY;
    }
    string appId = PermissionUtils::GetAppIdByBundleName(bundleName, uid);
    class MediafusePermCheckInfo infoR(target, MEDIA_FILEMODE_READONLY, fileId, appId, uid);
    permGranted = infoR.CheckPermission(tokenCaller, false);
    if (permGranted > 0) {
        return permGranted;
    }
    class MediafusePermCheckInfo infoW(target, MEDIA_FILEMODE_WRITEONLY, fileId, appId, uid);
    permGranted = infoW.CheckPermission(tokenCaller, false);
    return permGranted;
}

static int32_t GetCompatibleModeFromFileId(int32_t &compatibleMode, std::string &mimeType, const string &fileId)
{
    NativeRdb::RdbPredicates rdbPredicate(PhotoColumn::PHOTOS_TABLE);
    rdbPredicate.EqualTo(MediaColumn::MEDIA_ID, fileId);

    vector<string> columns;
    columns.push_back(MediaColumn::MEDIA_MIME_TYPE);
    columns.push_back(PhotoColumn::PHOTO_EXIST_COMPATIBLE_DUPLICATE);
    auto resultSet = MediaLibraryRdbStore::Query(rdbPredicate, columns);
    int32_t numRows = 0;
    if (resultSet == nullptr) {
        MEDIA_ERR_LOG("Failed to get rslt");
        return E_ERR;
    }
    int32_t ret = resultSet->GetRowCount(numRows);
    if ((ret != NativeRdb::E_OK) || (numRows <= 0)) {
        MEDIA_ERR_LOG("Failed to get filePath");
        return E_ERR;
    }
    if (resultSet->GoToFirstRow() == NativeRdb::E_OK) {
        mimeType = GetStringVal(MediaColumn::MEDIA_MIME_TYPE, resultSet);
        compatibleMode = GetInt32Val(PhotoColumn::PHOTO_EXIST_COMPATIBLE_DUPLICATE, resultSet);
    }
    return E_SUCCESS;
}

static bool IsHighPixelPicture(const string &fileId)
{
    NativeRdb::RdbPredicates rdbPredicate(PhotoColumn::PHOTOS_TABLE);
    rdbPredicate.EqualTo(MediaColumn::MEDIA_ID, fileId);
    vector<string> columns;
    columns.push_back(PhotoColumn::PHOTO_WIDTH);
    columns.push_back(PhotoColumn::PHOTO_HEIGHT);

    auto resultSet = MediaLibraryRdbStore::Query(rdbPredicate, columns);
    int32_t numRows = 0;
    if (resultSet == nullptr) {
        MEDIA_ERR_LOG("Failed to get result");
        return false;
    }
    int32_t ret = resultSet->GetRowCount(numRows);
    if (ret != NativeRdb::E_OK || numRows <= 0) {
        MEDIA_ERR_LOG("Failed to get numRows");
        return false;
    }
    int32_t width = 0;
    int32_t height = 0;
    if (resultSet->GoToFirstRow() == NativeRdb::E_OK) {
        width = GetInt32Val(PhotoColumn::PHOTO_WIDTH, resultSet);
        height = GetInt32Val(PhotoColumn::PHOTO_HEIGHT, resultSet);
    }
    if (IsHighPixel(width, height)) {
        return true;
    }
    return false;
}

static bool NeedTranscodeHighPixelPicture(bool isHighPixel, const int uid,
    const string &bundleName)
{
    if (isHighPixel) {
        CompatibleInfo compatibleInfo;
        auto ret = TranscodeCompatibleInfoOperation::QueryCompatibleInfo(bundleName, compatibleInfo);
        if (ret == E_OK && compatibleInfo.highResolution != -1) {
            return compatibleInfo.highResolution == 0;
        }
        bool isSystemApp = PermissionUtils::IsSystemAppByBundleName(bundleName);
        if (isSystemApp) {
            return false;
        }
        if (HeifTranscodingCheckUtils::CanSupportedHighPixelPicture(bundleName, HighPixelType::PIXEL_200)) {
            return false;
        }
        MEDIA_INFO_LOG("NeedTranscodeHighPixelPicture need transcode");
        return true;
    }
    return false;
}

static void SetTranscodeType(bool isHighPixel, bool isHeif, TranscodeType& transcodeType)
{
    if (isHeif) {
        if (isHighPixel) {
            transcodeType = TranscodeType::HIGH_PIXEL_HEIF;
            return;
        }
        transcodeType = TranscodeType::HEIF;
    } else {
        if (isHighPixel) {
            transcodeType = TranscodeType::HIGH_PIXEL;
            return;
        }
        transcodeType = TranscodeType::DEFAULT;
    }
}

static bool IsUriTranscoded(const string &realUri, const string &inputUri)
{
    if (inputUri.empty()) {
        return false;
    }
    return MediaFileUtils::GetExtensionFromPath(realUri) != MediaFileUtils::GetExtensionFromPath(inputUri);
}

static int32_t GetTranscodeUri(string &filePath, const string &mode,
    const int uid, TranscodeType& transcodeType, const string &uri)
{
    string fileId;
    GetFileIdFromUri(fileId, uri);
    string bundleName;
    PermissionUtils::GetClientBundle(uid, bundleName);
    CHECK_AND_RETURN_RET_LOG(mode == MEDIA_FILEMODE_READONLY, E_INNER_FAIL,
        "mode is not read only, filePath: %{private}s", filePath.c_str());
    int32_t compatibleMode = 0;
    std::string mimeType("");
    CHECK_AND_RETURN_RET_LOG(GetCompatibleModeFromFileId(compatibleMode, mimeType, fileId) == E_OK, E_INNER_FAIL,
        "Get compatible mode failed, fileId: %{public}s", fileId.c_str());
    bool isHighPixel = IsHighPixelPicture(fileId);
    bool isHeif = (mimeType == "image/heif" || mimeType == "image/heic");
    CHECK_AND_RETURN_RET_INFO_LOG(isHighPixel || isHeif, E_INNER_FAIL, "[transcode] not high, not heif");
    CHECK_AND_RETURN_RET_LOG(compatibleMode != 0, E_INNER_FAIL,
        "Is not have transcode file, filePath: %{private}s", filePath.c_str());
    string path = MediaEditUtils::GetEditDataDir(filePath);
    CHECK_AND_RETURN_RET_LOG(!path.empty(), E_INNER_FAIL,
        "Get edit data dir path failed, filePath: %{private}s", filePath.c_str());
    MEDIA_INFO_LOG("GetTranscodeUri path: %{private}s", path.c_str());
    string tempPath = path + "/transcode.jpg";
    CHECK_AND_RETURN_RET_LOG(MediaFileUtils::IsFileExists((tempPath)), E_INNER_FAIL, "transcode.jpg is not exist");

    if (IsUriTranscoded(filePath, uri)) {
        MEDIA_INFO_LOG("fileAsset uri is transcoded, fileAsset uri: %{public}s", uri.c_str());
        filePath = tempPath;
        SetTranscodeType(isHighPixel, isHeif, transcodeType);
        return E_OK;
    }

    auto ret = HeifTranscodingCheckUtils::CheckTranscodeMode(bundleName, isHighPixel, isHeif);
    CHECK_AND_RETURN_RET_INFO_LOG(ret != TranscodeMode::CURRENT,
        E_INNER_FAIL, "CheckTranscodeMode is CURRENT, bundleName: %{public}s", bundleName.c_str());
    if (ret == TranscodeMode::COMPATIBLE) {
        MEDIA_INFO_LOG("CheckTranscodeMode is COMPATIBLE, bundleName: %{public}s", bundleName.c_str());
        filePath = tempPath;
        SetTranscodeType(isHighPixel, isHeif, transcodeType);
        return E_OK;
    }

    if (!NeedTranscodeHighPixelPicture(isHighPixel, uid, bundleName)) {
        if (!isHeif) {
            MEDIA_INFO_LOG("Display name is not heif, filePath: %{private}s", filePath.c_str());
            return E_INNER_FAIL;
        }
        CHECK_AND_RETURN_RET_LOG(HeifTranscodingCheckUtils::CanSupportedCompatibleDuplicate(bundleName), E_INNER_FAIL,
            "Get client bundle name failed, filePath: %{private}s", filePath.c_str());
    }
    filePath = tempPath;
    SetTranscodeType(isHighPixel, isHeif, transcodeType);
    return E_OK;
}

int32_t MediaFuseManager::DoGetAttr(const char *path, struct stat *stbuf)
{
    string fileId;
    string storageName;
    string displayName;
    string target = path;
    bool cond = (path == nullptr || strlen(path) == 0);

    CHECK_AND_RETURN_RET_LOG(!cond, E_ERR, "Invalid path, %{public}s", path == nullptr ? "null" : path);
    int32_t ret;
    int32_t splitCount = countSubString(path, "/");
    if (splitCount != URI_SLASH_NUM_API10) {
        ret = lstat(FUSE_ROOT_MEDIA_DIR.c_str(), stbuf);
    } else {
        fuse_context *ctx = fuse_get_context();
#ifdef MEDIALIBRARY_SECURE_ALBUM_ENABLE
        if (ctx != nullptr) {
            int32_t criticalCheck = PermissionCheck::CheckCriticalPhotoPermission(fileId, ctx->uid);
            if (criticalCheck != E_SUCCESS) {
                return E_PERMISSION_DENIED;
            }
        }
#endif
        ret = GetMessageFromUriForGetAttr(fileId, storageName, displayName, path);
        CHECK_AND_RETURN_RET_LOG(ret == E_SUCCESS, E_ERR, "get attr message fail");
        MEDIA_INFO_LOG("check fileId = %{public}s", fileId.c_str());
        CloudAssetTimeQueryParams cloudAssetTimeQueryParams(fileId, 0, 0, 0);
        PathValidationParams pathValidationParams(fileId, "", "", FileSourceType::MEDIA, 0);
        ret = GetPathFromFileIdForGetAttr(target, fileId, cloudAssetTimeQueryParams, pathValidationParams);
        CHECK_AND_RETURN_RET_LOG(ret == E_SUCCESS, FILE_FAIL, "get attr path fail");
        CHECK_AND_RETURN_RET_LOG(ctx != nullptr, E_INNER_FAIL, "fuse_get_context returned nullptr");
        ret = CheckMediaLibraryPathValidity(target, fileId, storageName, pathValidationParams, displayName);
        CHECK_AND_RETURN_RET_LOG(ret == E_SUCCESS, FILE_FAIL, "check attr path validity fail");
        int32_t permGranted = DoMedialibraryReadPermission(fileId, target, ctx->uid);
        CHECK_AND_RETURN_RET_LOG(permGranted > 0, E_PERMISSION_DENIED, "permission denied");
        CHECK_AND_RETURN_RET_LOG(MediaFileUtils::IsFileExists(target), FILE_FAIL, "file is not exist.");
        TranscodeType type;
        GetTranscodeUri(target, MEDIA_FILEMODE_READONLY, ctx->uid, type, path);
        ret = lstat(target.c_str(), stbuf);
        if (ret == E_SUCCESS && cloudAssetTimeQueryParams.position == PHOTO_POSITION_TYPE_CLOUD) {
            stbuf->st_atim.tv_sec = cloudAssetTimeQueryParams.accesstime;
            stbuf->st_ctim.tv_sec = cloudAssetTimeQueryParams.changeTime;
            stbuf->st_mtim.tv_sec = cloudAssetTimeQueryParams.changeTime;
        }
    }
    stbuf->st_mode = stbuf->st_mode | 0x6;
    MEDIA_DEBUG_LOG("get attr succ");
    return ret;
}

int32_t MediafusePermCheckInfo::WrCheckPermission(const string &filePath, const string &mode,
    const uid_t &uid, AccessTokenID &tokenCaller, bool isNeedRecord)
{
    vector<string> perms;
    bool containsRead = false;
    if (mode.find("r") != string::npos) {
        perms.push_back(PERM_READ_IMAGEVIDEO);
        containsRead = true;
    }
    if (mode.find("w") != string::npos) {
        perms.push_back(PERM_WRITE_IMAGEVIDEO);
    }
    if (!isNeedRecord) {
        if (!PermissionUtils::CheckPhotoCallerPermissionNoRecord(perms, uid, tokenCaller)) {
            return E_PERMISSION_DENIED;
        }
        if (containsRead) {
            return CloudReadPermissionCheck::CheckPureCloudAssets(fileId_);
        }
        return E_SUCCESS;
    }

    OpenDataInfo openData;
    openData.uri = openUri_;
    openData.uid = uid;
    openData.userId = uid / PermissionUtils::BASE_USER_RANGE;
    openData.type = "open";
    openData.timestamp = MediaFileUtils::UTCTimeMilliSeconds();
    if (!PermissionUtils::CheckPhotoCallerPermission(perms, uid, tokenCaller, openData)) {
        return E_PERMISSION_DENIED;
    }
    if (containsRead) {
        return CloudReadPermissionCheck::CheckPureCloudAssets(fileId_);
    }
    return E_SUCCESS;
}
 
void MediafusePermCheckInfo::SetOpenUri(const std::string &openUri)
{
    openUri_ = openUri;
}

static bool CheckPermissionType(const vector<int32_t> currentTypes, const set<int32_t> targetTypes)
{
    for (int32_t type : currentTypes) {
        if (targetTypes.count(type) > 0) {
            return true;
        }
    }
    return false;
}

static int32_t DbCheckPermission(const string &filePath, const string &mode, const string &fileId,
    const string &appId, const AccessTokenID &tokenCaller)
{
    if (appId.empty() || fileId.empty() || (tokenCaller == INVALID_TOKENID)) {
        MEDIA_ERR_LOG("invalid input");
        return E_PERMISSION_DENIED;
    }
    NativeRdb::RdbPredicates rdbPredicate(TABLE_PERMISSION);
    rdbPredicate.EqualTo("file_id", fileId);
    rdbPredicate.And()->BeginWrap()->EqualTo("appid", appId)
        ->Or()->EqualTo("target_tokenId", to_string(tokenCaller))->EndWrap();
    vector<string> columns;
    columns.push_back(FIELD_PERMISSION_TYPE);
    columns.push_back("file_id");
    columns.push_back("appid");
    columns.push_back("target_tokenId");
    auto resultSet = MediaLibraryRdbStore::Query(rdbPredicate, columns);
    CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, E_PERMISSION_DENIED, "Failed to get permission type");
    vector<int32_t> permissionTypes;
    while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
        int32_t permissionType = GetInt32Val(FIELD_PERMISSION_TYPE, resultSet);
        permissionTypes.push_back(permissionType);
        MEDIA_INFO_LOG("get permissionType %{public}d", permissionType);
    }
    bool cond = ((mode.find("r") != string::npos) &&
        (!CheckPermissionType(permissionTypes, AppUriPermissionColumn::PERMISSION_TYPE_READ)));
    CHECK_AND_RETURN_RET(!cond, E_PERMISSION_DENIED);
    cond = ((mode.find("w") != string::npos) &&
        (!CheckPermissionType(permissionTypes, AppUriPermissionColumn::PERMISSION_TYPE_WRITE)));
    CHECK_AND_RETURN_RET(!cond, E_PERMISSION_DENIED);
    return E_SUCCESS;
}

bool MediafusePermCheckInfo::CheckPermission(uint32_t &tokenCaller, bool isNeedRecord)
{
    int err = WrCheckPermission(filePath_, mode_, uid_, tokenCaller, isNeedRecord);
    bool rslt;
    if (err == E_SUCCESS) {
        MEDIA_INFO_LOG("wr check succ");
        return true;
    }
    err = DbCheckPermission(filePath_, mode_, fileId_, appId_, tokenCaller);
    if (err == E_SUCCESS) {
        MEDIA_INFO_LOG("db check succ");
        rslt = true;
    } else {
        rslt = false;
    }
    OpenDataInfo openData;
    openData.uri = openUri_;
    openData.uid = uid_;
    openData.userId = uid_ / PermissionUtils::BASE_USER_RANGE;
    openData.type = "open";
    openData.timestamp = MediaFileUtils::UTCTimeMilliSeconds();
    if (mode_.find("r") != string::npos && isNeedRecord) {
        PermissionUtils::CollectPermissionInfo(PERM_READ_IMAGEVIDEO, rslt,
            PermissionUsedTypeValue::PICKER_TYPE, uid_, openData);
    }
    if (mode_.find("w") != string::npos && isNeedRecord) {
        PermissionUtils::CollectPermissionInfo(PERM_WRITE_IMAGEVIDEO, rslt,
            PermissionUsedTypeValue::PICKER_TYPE, uid_, openData);
    }
    return rslt;
}

static int32_t OpenFile(const string &filePath, const string &fileId, const string &mode,
    const string &uri)
{
    MEDIA_DEBUG_LOG("fuse open file");
    fuse_context *ctx = fuse_get_context();
    CHECK_AND_RETURN_RET_LOG(ctx != nullptr, E_INNER_FAIL, "fuse_get_context returned nullptr");
#ifdef MEDIALIBRARY_SECURE_ALBUM_ENABLE
    int32_t criticalCheck = CheckCriticalPhotoPermission(fileId, ctx->uid);
    if (criticalCheck != E_SUCCESS) {
        return E_PERMISSION_DENIED;
    }
#endif
    uid_t uid = ctx->uid;
    string bundleName;
    AccessTokenID tokenCaller = INVALID_TOKENID;
    PermissionUtils::GetClientBundle(uid, bundleName);
    string appId = PermissionUtils::GetAppIdByBundleName(bundleName, uid);
    class MediafusePermCheckInfo info(filePath, mode, fileId, appId, uid);
    info.SetOpenUri(uri);
    bool permGranted = info.CheckPermission(tokenCaller);
    if (!permGranted) {
        return E_ERR;
    }
    TranscodeType transcodeType = TranscodeType::DEFAULT;
    string path = filePath;
    int32_t err = GetTranscodeUri(path, mode, uid, transcodeType, uri);
    int32_t ret = MediaPrivacyManager(path, mode, fileId, appId, bundleName, uid, tokenCaller).Open();
    if (err == 0 && ret >= 0) {
        MEDIA_INFO_LOG("libc open transcode file success");
        auto dfxManager = DfxManager::GetInstance();
        CHECK_AND_EXECUTE(dfxManager != nullptr, close(ret));
        CHECK_AND_RETURN_RET_LOG(dfxManager != nullptr, E_INNER_FAIL, "DfxManager::GetInstance() returned nullptr");
        dfxManager->HandleTranscodeAccessTime(ACCESS_LIBC, transcodeType);
    }
    return ret;
}

static int32_t HasTransCodeFile(const string &filePath, const string &fileId)
{
    int32_t compatibleMode = 0;
    std::string mimeType("");
    if (GetCompatibleModeFromFileId(compatibleMode, mimeType, fileId) != E_SUCCESS) {
        MEDIA_ERR_LOG("Get compatible mode failed, fileId: %{public}s", fileId.c_str());
        return E_ERR;
    }
    CHECK_AND_RETURN_RET_LOG(compatibleMode != 0, E_INNER_FAIL,
        "Is not have transcode file, filePath: %{private}s", filePath.c_str());
    return E_OK;
}

static int32_t GetFileMtime(const string &filePath, time_t &mtime)
{
    struct stat statInfo {};
    if (stat(filePath.c_str(), &statInfo) != 0) {
        MEDIA_ERR_LOG("Get file mtime failed, path = %{public}s",
            MediaFileUtils::DesensitizePath(filePath).c_str());
        return E_ERR;
    }
    mtime = statInfo.st_mtime;
    return E_OK;
}

int32_t MediaFuseManager::DoOpen(const char *path, int flags, int &fd)
{
    uint32_t realFlag = static_cast<uint32_t>(flags) & (O_RDONLY | O_WRONLY | O_RDWR | O_TRUNC | O_APPEND);
    string fileId;
    string target;
    if (MEDIA_OPEN_MODE_MAP.find(realFlag) == MEDIA_OPEN_MODE_MAP.end()) {
        MEDIA_ERR_LOG("Open mode err, flag = %{public}u", realFlag);
        return E_ERR;
    }
    GetFileIdFromUri(fileId, path);
    GetPathFromFileId(target, fileId);
    MEDIA_DEBUG_LOG("MediaFuseManager::DoOpen AddVisitCount fileId[%{public}s]", fileId.c_str());
    MediaVisitCountManager::AddVisitCount(MediaVisitCountManager::VisitCountType::PHOTO_FS, fileId);
    fd = OpenFile(target, fileId, MEDIA_OPEN_MODE_MAP.at(realFlag), path);
    if (fd < 0) {
        MEDIA_ERR_LOG("Open failed, path = %{private}s, errno = %{public}d", target.c_str(), errno);
        return E_ERR;
    }
    time_t mtime = 0;
    if (realFlag == O_RDONLY || HasTransCodeFile(target, fileId) != E_OK || GetFileMtime(target, mtime) != E_OK) {
        return E_OK;
    }
    MEDIA_OPEN_WRITE_MAP.Insert(fd, mtime);
    return 0;
}

int32_t MediaFuseManager::DoRelease(const char *path, const int &fd)
{
    string fileId;
    string filePath;
    GetFileIdFromUri(fileId, path);
    GetPathFromFileId(filePath, fileId);
    if (fd < 0) {
        MEDIA_ERR_LOG("fuse close file fail");
        return E_ERR;
    }
    time_t oldMtime = 0;
    if (MEDIA_OPEN_WRITE_MAP.Find(fd, oldMtime)) {
        MEDIA_OPEN_WRITE_MAP.Erase(fd);
        time_t newMtime = 0;
        if (GetFileMtime(filePath, newMtime) != E_OK) {
            MEDIA_ERR_LOG("Get file mtime failed, path = %{private}s", filePath.c_str());
            close(fd);
            return E_ERR;
        }
        if (oldMtime != newMtime) {
            MediaLibraryTranscodeDataAgingOperation::DeleteTransCodeInfo(filePath, fileId, __func__);
        }
    }
    close(fd);
    MediaLibraryObjectUtils::ScanFileAsync(filePath, fileId, MediaLibraryApi::API_10);
    MEDIA_DEBUG_LOG("fuse close file succ");
    return E_OK;
}

int32_t MediaFuseManager::MountFuse(std::string &mountpoint)
{
    int devFd = -1;
    // get user id
    int32_t userId =  static_cast<int32_t>(getuid() / BASE_USER_RANGE);

    // mount fuse
    auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    CHECK_AND_RETURN_RET_LOG(samgr != nullptr, E_FAIL, "Get system ability mgr failed.");

    auto remote = samgr->GetSystemAbility(STORAGE_MANAGER_MANAGER_ID);
    CHECK_AND_RETURN_RET_LOG(remote != nullptr, E_FAIL, "GetSystemAbility Service Failed.");

    sptr<StorageManager::IStorageManager> proxy_ = iface_cast<StorageManager::IStorageManager>(remote);
    int32_t err = proxy_->MountMediaFuse(userId, devFd);
    CHECK_AND_RETURN_RET_LOG(err == E_OK, err, "Mount failed for media fuse daemon, err = %{public}d", err);

    mountpoint = "/dev/fd/" + std::to_string(devFd);
    return E_OK;
}

int32_t MediaFuseManager::UMountFuse()
{
    // get user id
    int32_t userId =  static_cast<int32_t>(getuid() / BASE_USER_RANGE);

    // umount fuse
    auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    CHECK_AND_RETURN_RET_LOG(samgr != nullptr, E_FAIL, "Get system ability mgr failed.");

    auto remote = samgr->GetSystemAbility(STORAGE_MANAGER_MANAGER_ID);
    CHECK_AND_RETURN_RET_LOG(remote != nullptr, E_FAIL, "GetSystemAbility Service Failed.");

    sptr<StorageManager::IStorageManager> proxy_ = iface_cast<StorageManager::IStorageManager>(remote);
    CHECK_AND_RETURN_RET_LOG(proxy_ != nullptr, E_FAIL, "Create IStorageManager Proxy failed.");

    int32_t err = proxy_->UMountMediaFuse(userId);
    CHECK_AND_RETURN_RET_LOG(err == E_OK, err,
        "UMount failed for media fuse daemon, err = %{public}d", err);
    return E_OK;
}

int32_t MediaFuseManager::DoHdcGetAttr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
{
    MEDIA_INFO_LOG("Hdc getattr start, path = %{private}s", path);
    if (fi) {
        return MediaFuseHdcOperations::HandleFstat(fi, stbuf);
    }

    int32_t res = MediaFuseHdcOperations::HandleRootOrPhoto(path, stbuf);
    if (res == E_SUCCESS) {
        return res;
    }

    vector<string> args;
    res = MediaFuseHdcOperations::GetArgs(path, args);
    CHECK_AND_RETURN_RET_LOG(res == E_SUCCESS, E_ERR, "GetArgs fail.");
    if (args.size() < HDC_SECOND_ARGS) {
        MEDIA_ERR_LOG("Invalid path.");
        return E_ERR;
    }

    int32_t albumId = -1;
    string localPath;
    if (args.size() == HDC_SECOND_ARGS) {
        int32_t result = MediaFuseHdcOperations::HandlePhotoPath(args[HDC_FIRST_ARGS], albumId, localPath, stbuf);
        if (result != E_SUCCESS) {
            return result;
        }
        return E_SUCCESS;
    }

    if (args.size() > HDC_THIRD_ARGS || !MediaFuseHdcOperations::IsImageOrVideoFile(args[HDC_SECOND_ARGS])) {
        MEDIA_ERR_LOG("Invalid path.");
        return E_ERR;
    }

    res = MediaFuseHdcOperations::HandleFilePath(args, albumId, localPath);
    if (res != E_SUCCESS) {
        return res;
    }

    res = MediaFuseHdcOperations::HandleLstat(localPath, stbuf);
    if (res != E_SUCCESS) {
        return res;
    }
    return E_SUCCESS;
}

int32_t MediaFuseManager::DoHdcOpen(const char *path, int flags, int &fd)
{
    MEDIA_INFO_LOG("hdc open start, path = %{private}s", path);
    if (path == nullptr || strlen(path) == 0) {
        MEDIA_ERR_LOG("Invalid path");
        return -EINVAL;
    }
    string target = path;
    int32_t albumId = -1;
    string filePath;
    string displayName;
    int32_t res = MediaFuseHdcOperations::Parse(target, albumId, filePath, displayName);
    CHECK_AND_RETURN_RET_LOG(res == E_SUCCESS, E_ERR, "Parse fail");
    bool isMovingPhoto = false;
    string tempPath;
    if (filePath.empty()) {
        // handle moving photo mp4
        res = MediaFuseHdcOperations::HandleMovingPhoto(filePath, displayName, albumId);
        CHECK_AND_RETURN_RET_LOG(res == E_SUCCESS, E_ERR, "HandleMovingPhoto fail");
        res = MediaFuseHdcOperations::GetPathFromDisplayname(displayName, albumId, filePath);
        CHECK_AND_RETURN_RET_LOG(res == E_SUCCESS, E_ERR, "GetPathFromDisplayname fail");
        isMovingPhoto = true;
        tempPath = filePath;
        filePath = MovingPhotoFileUtils::GetMovingPhotoVideoPath(filePath);
    }
    if (static_cast<uint>(flags) & (O_CREAT | O_WRONLY)) {
        if (isMovingPhoto) {
            filePath = tempPath;
            displayName = MediaFuseHdcOperations::JpgToMp4(displayName);
        }
        res = MediaFuseHdcOperations::DeletePhotoByFilePath(filePath);
        CHECK_AND_RETURN_RET_LOG(res == E_SUCCESS, E_ERR, "Delete failed");
        MEDIA_CREATE_WRITE_MAP.EnsureInsert(target, false);
        res = MediaFuseHdcOperations::CreateFd(displayName, albumId, fd);
        CHECK_AND_RETURN_RET_LOG(fd > 0, E_ERR, "MediaLibraryPhotoOperations::Create failed, path = %{private}s",
            filePath.c_str());
        MEDIA_CREATE_WRITE_MAP.EnsureInsert(target, true);
        return E_SUCCESS;
    }
    string localPath;
    res = MediaFuseHdcOperations::ConvertToLocalPhotoPath(filePath, localPath);
    CHECK_AND_RETURN_RET_LOG(res == E_SUCCESS, E_ERR, "ConvertToLocalPhotoPath failed");
    char realPath[PATH_MAX] = {0};
    bool bflag = realpath(localPath.c_str(), realPath) == nullptr;
    CHECK_AND_RETURN_RET_LOG(!bflag, E_ERR,
        "check dirPath fail, dirPath = %{private}s", localPath.c_str());
    fd = open(realPath, flags);
    CHECK_AND_RETURN_RET_LOG(fd >= 0, E_ERR, "Open failed, localPath=%{private}s, errno=%{public}d",
        localPath.c_str(), -errno);
    return E_SUCCESS;
}

int32_t MediaFuseManager::DoHdcCreate(const char *path, mode_t mode, struct fuse_file_info *fi)
{
    MEDIA_INFO_LOG("hdc create file start, path=%{private}s", path);
    if (path == nullptr || strlen(path) == 0) {
        MEDIA_ERR_LOG("Invalid path");
        return -EINVAL;
    }
    string target = path;
    MEDIA_CREATE_WRITE_MAP.EnsureInsert(target, false);

    int32_t albumId = -1;
    string filePath;
    string displayName;
    int32_t res = MediaFuseHdcOperations::Parse(target, albumId, filePath, displayName);
    CHECK_AND_RETURN_RET_LOG(res == E_SUCCESS, E_ERR, "Parse fail");

    int32_t fd;
    res = MediaFuseHdcOperations::CreateFd(displayName, albumId, fd);
    if (fd <= 0) {
        MEDIA_ERR_LOG("MediaLibraryPhotoOperations::Create failed, path = %{private}s", filePath.c_str());
        return res;
    }
    fi->fh = static_cast<uint64_t>(fd);
    MEDIA_CREATE_WRITE_MAP.EnsureInsert(target, true);
    return E_SUCCESS;
}

int32_t MediaFuseManager::DoHdcRelease(const char *path, const int32_t &fd)
{
    MEDIA_INFO_LOG("hdc release start, path=%{private}s.", path);
    if (path == nullptr || strlen(path) == 0) {
        MEDIA_ERR_LOG("Invalid path");
        return -EINVAL;
    }

    if (fd < 0) {
        MEDIA_ERR_LOG("Invalid fd (negative), path=%{private}s, fd=%{private}d", path, fd);
        return -EBADF;
    }

    if (close(fd) == -1) {
        MEDIA_ERR_LOG("Close fd failed, path=%{private}s, fd=%{private}d, errno=%{public}d", path, fd, errno);
        return -errno;
    }

    string target = path;
    bool isCreateWrite = false;
    if (!MEDIA_CREATE_WRITE_MAP.Find(target, isCreateWrite)) {
        MEDIA_INFO_LOG("not found, path=%{private}s, try do release.", path);
        int32_t ret = DoRelease(path, static_cast<int>(fd));
        CHECK_AND_RETURN_RET_LOG(ret == E_SUCCESS, E_ERR, "do release fail");
        return E_SUCCESS;
    }

    if (isCreateWrite) {
        int32_t res = MediaFuseHdcOperations::ScanFileByPath(target);
        MEDIA_CREATE_WRITE_MAP.Erase(target);
        return res;
    } else {
        MEDIA_ERR_LOG("DoHdcCreate failed.");
        MEDIA_CREATE_WRITE_MAP.Erase(target);
        return E_ERR;
    }
}

int32_t MediaFuseManager::DoHdcUnlink(const char *path)
{
    MEDIA_INFO_LOG("Unlink file start, path=%{private}s.", path);
    if (path == nullptr || strlen(path) == 0) {
        MEDIA_ERR_LOG("Invalid path");
        return E_ERR;
    }

    string target = path;
    int32_t albumId = -1;
    string filePath;
    string displayName;
    int32_t res = MediaFuseHdcOperations::Parse(target, albumId, filePath, displayName);
    CHECK_AND_RETURN_RET_LOG(res == E_SUCCESS, E_ERR, "Parse fail");

    string fileId;
    if (filePath.empty()) {
        res = MediaFuseHdcOperations::HandleMovingPhoto(filePath, displayName, albumId);
        if (res == E_NO_SUCH_FILE) {
            MEDIA_INFO_LOG("MovingPhoto mp4 has deleted");
            return E_SUCCESS;
        }
        CHECK_AND_RETURN_RET_LOG(res == E_SUCCESS, E_ERR, "HandleMovingPhoto fail");
        res = MediaFuseHdcOperations::GetPathFromDisplayname(displayName, albumId, filePath);
        CHECK_AND_RETURN_RET_LOG(res == E_SUCCESS, E_ERR, "GetPathFromDisplayname fail");
    }
    int ret = MediaFuseHdcOperations::DeletePhotoByFilePath(filePath);
    if (ret != 0) {
        MEDIA_ERR_LOG("Unlink failed");
        return ret;
    }
    return E_SUCCESS;
}

int32_t MediaFuseManager::DoHdcReadDir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
    enum fuse_readdir_flags flags)
{
    MEDIA_INFO_LOG("hdc readdir start, path=%{private}s.", path);
    if (path == nullptr || strlen(path) == 0) {
        MEDIA_ERR_LOG("Invalid path");
        return -EINVAL;
    }

    string target = path;
    if (target == "/") {
        return MediaFuseHdcOperations::ReadPhotoRootDir(buf, filler, offset);
    }

    if (target.find("/") == 0) {
        return MediaFuseHdcOperations::ReadAlbumDir(target, buf, filler, offset);
    }

    MEDIA_ERR_LOG("Invalid path format: %{private}s", path);
    return -EINVAL;
}

} // namespace Media
} // namespace OHOS