* Copyright (C) 2024 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 "FileUtils"
#include "ringtone_file_utils.h"
#include <dirent.h>
#include <fcntl.h>
#include <ftw.h>
#include <fstream>
#include <sstream>
#include <sys/sendfile.h>
#include <system_error>
#include <unistd.h>
#include <iostream>
#include "directory_ex.h"
#include "ringtone_db_const.h"
#include "ringtone_errno.h"
#include "ringtone_log.h"
#include "ringtone_mimetype_utils.h"
#include "ringtone_type.h"
#include "ringtone_xcollie.h"
#include "vibrate_type.h"
#include "securec.h"
#include "unique_fd.h"
namespace OHOS {
namespace Media {
using namespace std;
static const int32_t OPEN_FDS = 128;
static const mode_t MODE_RWX_USR_GRP = 02771;
static const mode_t MODE_RW_USR = 0644;
const std::string OLD_RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH = "/storage/media/local/files/Ringtone";
const vector<string> EXIF_SUPPORTED_EXTENSION = {
RINGTONE_CONTAINER_TYPE_3GA,
RINGTONE_CONTAINER_TYPE_AC3,
RINGTONE_CONTAINER_TYPE_A52,
RINGTONE_CONTAINER_TYPE_AMR,
RINGTONE_CONTAINER_TYPE_IMY,
RINGTONE_CONTAINER_TYPE_RTTTL,
RINGTONE_CONTAINER_TYPE_XMF,
RINGTONE_CONTAINER_TYPE_RTX,
RINGTONE_CONTAINER_TYPE_MXMF,
RINGTONE_CONTAINER_TYPE_M4A,
RINGTONE_CONTAINER_TYPE_M4B,
RINGTONE_CONTAINER_TYPE_M4P,
RINGTONE_CONTAINER_TYPE_F4A,
RINGTONE_CONTAINER_TYPE_F4B,
RINGTONE_CONTAINER_TYPE_F4P,
RINGTONE_CONTAINER_TYPE_M3U,
RINGTONE_CONTAINER_TYPE_SMF,
RINGTONE_CONTAINER_TYPE_MKA,
RINGTONE_CONTAINER_TYPE_RA,
RINGTONE_CONTAINER_TYPE_MP3,
RINGTONE_CONTAINER_TYPE_AAC,
RINGTONE_CONTAINER_TYPE_ADTS,
RINGTONE_CONTAINER_TYPE_ADT,
RINGTONE_CONTAINER_TYPE_SND,
RINGTONE_CONTAINER_TYPE_FLAC,
RINGTONE_CONTAINER_TYPE_MP2,
RINGTONE_CONTAINER_TYPE_MP1,
RINGTONE_CONTAINER_TYPE_MPA,
RINGTONE_CONTAINER_TYPE_M4R,
RINGTONE_CONTAINER_TYPE_WAV,
RINGTONE_CONTAINER_TYPE_OGG,
RINGTONE_CONTAINER_TYPE_VIDEO_MP4
};
bool RingtoneFileUtils::IsTargetExtension(const string &path)
{
const string ext = RingtoneFileUtils::GetExtensionFromPath(path);
std::string mimeType = RingtoneMimeTypeUtils::GetMimeTypeFromExtension(ext);
int32_t mime = RingtoneMimeTypeUtils::GetMediaTypeFromMimeType(mimeType);
if (mime == RINGTONE_MEDIA_TYPE_AUDIO) {
return true;
} else if (mime == RINGTONE_MEDIA_TYPE_VIDEO) {
return true;
}
RINGTONE_ERR_LOG("MimeType error:%{public}s,%{public}s", ext.c_str(), mimeType.c_str());
bool ret = find(EXIF_SUPPORTED_EXTENSION.begin(), EXIF_SUPPORTED_EXTENSION.end(), ext) !=
EXIF_SUPPORTED_EXTENSION.end();
if (!ret) {
RINGTONE_ERR_LOG("invalid target extension:%{public}s", ext.c_str());
}
return ret;
}
static bool IsVibrateFile(const string &path)
{
const string ext = RingtoneFileUtils::GetExtensionFromPath(path);
bool ret = (ext == VIBRATE_CONTAINER_TYPE_JSON);
if (!ret) {
RINGTONE_ERR_LOG("invalid target extension:%{public}s", ext.c_str());
}
return ret;
}
string RingtoneFileUtils::SplitByChar(const string &str, const char split)
{
size_t splitIndex = str.find_last_of(split);
return (splitIndex == string::npos) ? ("") : (str.substr(splitIndex + 1));
}
string RingtoneFileUtils::GetExtensionFromPath(const string &path)
{
string extention = SplitByChar(path, '.');
if (!extention.empty()) {
transform(extention.begin(), extention.end(), extention.begin(), ::tolower);
}
return extention;
}
string RingtoneFileUtils::GetFileNameFromPath(const string &path)
{
string fileName = {};
size_t found = path.rfind("/");
if (found != string::npos && (found + 1) < path.size()) {
fileName = path.substr(found + 1);
} else {
fileName = "";
}
return fileName;
}
static string ParseFromUri(const string& path, const string& key)
{
RINGTONE_INFO_LOG("parsing uri : %{public}s for key : %{public}s", path.c_str(), key.c_str());
auto keyLen = key.size();
auto found = path.find(key);
if (found == string::npos) {
RINGTONE_INFO_LOG("there is no such field in uri: %{public}s", path.c_str());
return "";
}
string sub = path.substr(found + keyLen + 1);
found = sub.find("&");
if (found != string::npos) {
sub = sub.substr(0, found);
}
sub = RingtoneFileUtils::UrlDecode(sub);
RINGTONE_INFO_LOG("parsing uri : %{public}s -> key=%{public}s, value=%{public}s",
path.c_str(), key.c_str(), sub.c_str());
return sub;
}
string RingtoneFileUtils::GetFileNameFromPathOrUri(const string &path, bool &isTitle)
{
string fileName = {};
size_t found = path.find("content://");
if (found == 0) {
fileName = ParseFromUri(path, "title");
isTitle = true;
} else {
fileName = GetFileNameFromPath(path);
isTitle = false;
}
RINGTONE_INFO_LOG("%{public}s -> %{public}s", path.c_str(), fileName.c_str());
return fileName;
}
string RingtoneFileUtils::GetBaseNameFromPath(const string &path)
{
size_t found = path.rfind("/");
size_t foundDot = path.rfind(".");
string baseName = {};
found = (found == string::npos ? 0 : found);
if ((foundDot > found) && (foundDot != string::npos)) {
baseName = path.substr(found + 1, foundDot - found - 1);
} else {
baseName = "";
}
return baseName;
}
bool RingtoneFileUtils::IsSameFile(const string &srcPath, const string &dstPath)
{
struct stat srcStatInfo {};
struct stat dstStatInfo {};
if (access(srcPath.c_str(), F_OK) || access(dstPath.c_str(), F_OK)) {
return false;
}
if (stat(srcPath.c_str(), &srcStatInfo) != 0) {
RINGTONE_ERR_LOG("Failed to get file %{private}s StatInfo, err=%{public}d", srcPath.c_str(), errno);
return false;
}
if (stat(dstPath.c_str(), &dstStatInfo) != 0) {
RINGTONE_ERR_LOG("Failed to get file %{private}s StatInfo, err=%{public}d", dstPath.c_str(), errno);
return false;
}
if (srcStatInfo.st_size != dstStatInfo.st_size) {
RINGTONE_INFO_LOG("Size differs, srcStatInfo.st_size != dstStatInfo.st_size");
return false;
}
return true;
}
static int32_t UnlinkCb(const char *fpath, const struct stat *sb, int32_t typeflag, struct FTW *ftwbuf)
{
CHECK_AND_RETURN_RET_LOG(fpath != nullptr, E_FAIL, "fpath == nullptr");
int32_t errRet = remove(fpath);
if (errRet != 0) {
RINGTONE_ERR_LOG("Failed to remove errno: %{public}d, path: %{private}s", errno, fpath);
}
return errRet;
}
int32_t RingtoneFileUtils::RemoveDirectory(const string &path)
{
return nftw(path.c_str(), UnlinkCb, OPEN_FDS, FTW_DEPTH | FTW_PHYS);
}
bool RingtoneFileUtils::Mkdir(const string &subStr, shared_ptr<int> errCodePtr)
{
mode_t mask = umask(0);
if (mkdir(subStr.c_str(), MODE_RWX_USR_GRP) == -1) {
if (errCodePtr != nullptr) {
*errCodePtr = errno;
}
RINGTONE_ERR_LOG("Failed to create directory %{public}d", errno);
umask(mask);
return (errno == EEXIST) ? true : false;
}
umask(mask);
return true;
}
bool RingtoneFileUtils::IsDirectory(const string &dirName, shared_ptr<int> errCodePtr)
{
struct stat statInfo {};
if (stat(dirName.c_str(), &statInfo) == 0) {
if (statInfo.st_mode & S_IFDIR) {
return true;
}
} else if (errCodePtr != nullptr) {
*errCodePtr = errno;
return false;
}
return false;
}
bool RingtoneFileUtils::CreateDirectory(const string &dirPath, shared_ptr<int> errCodePtr)
{
string subStr;
string segment;
stringstream folderStream(dirPath);
while (getline(folderStream, segment, '/')) {
if (segment.empty()) {
continue;
}
subStr.append(RINGTONE_SLASH_CHAR + segment);
if (!IsDirectory(subStr, errCodePtr)) {
if (!Mkdir(subStr, errCodePtr)) {
return false;
}
}
}
return true;
}
int32_t RingtoneFileUtils::OpenFile(const string &filePath, const string &mode)
{
int32_t errCode = E_ERR;
if (filePath.empty() || mode.empty()) {
RINGTONE_ERR_LOG("Invalid open argument! mode: %{private}s, path: %{private}s", mode.c_str(), filePath.c_str());
return errCode;
}
if (!RingtoneFileUtils::IsTargetExtension(filePath)) {
return E_INVALID_PATH;
}
static const unordered_map<string, int32_t> RINGTONE_OPEN_MODE_MAP = {
{ RINGTONE_FILEMODE_READONLY, O_RDONLY },
{ RINGTONE_FILEMODE_WRITEONLY, O_WRONLY },
{ RINGTONE_FILEMODE_READWRITE, O_RDWR },
{ RINGTONE_FILEMODE_WRITETRUNCATE, O_WRONLY | O_TRUNC },
{ RINGTONE_FILEMODE_WRITEAPPEND, O_WRONLY | O_APPEND },
{ RINGTONE_FILEMODE_READWRITETRUNCATE, O_RDWR | O_TRUNC },
{ RINGTONE_FILEMODE_READWRITEAPPEND, O_RDWR | O_APPEND },
};
if (RINGTONE_OPEN_MODE_MAP.find(mode) == RINGTONE_OPEN_MODE_MAP.end()) {
return E_ERR;
}
if (filePath.size() >= PATH_MAX) {
RINGTONE_ERR_LOG("File path too long %{public}d", (int)filePath.size());
return errCode;
}
string absFilePath;
RingtoneXCollie ringtoneXCollie("PathToRealPath time out",
[](void *) {
RINGTONE_INFO_LOG("PathToRealPath time out");
});
if (!PathToRealPath(filePath, absFilePath)) {
RINGTONE_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
ringtoneXCollie.CancelXCollieTimer();
return errCode;
}
ringtoneXCollie.CancelXCollieTimer();
if (absFilePath.empty()) {
RINGTONE_ERR_LOG("Failed to obtain the canonical path for source path %{public}d %{private}s",
errno, filePath.c_str());
return errCode;
}
RINGTONE_INFO_LOG("File absFilePath is %{private}s", absFilePath.c_str());
if (absFilePath.size() >= PATH_MAX) {
RINGTONE_ERR_LOG("abs file path too long %{public}d", (int)absFilePath.size());
return errCode;
}
return open(absFilePath.c_str(), RINGTONE_OPEN_MODE_MAP.at(mode));
}
int32_t RingtoneFileUtils::OpenVibrateFile(const std::string &filePath, const std::string &mode)
{
int32_t errCode = E_ERR;
if (filePath.empty() || mode.empty()) {
RINGTONE_ERR_LOG("Invalid open argument! mode: %{private}s, path: %{private}s", mode.c_str(), filePath.c_str());
return errCode;
}
if (!IsVibrateFile(filePath)) {
return E_INVALID_PATH;
}
static const unordered_map<string, int32_t> RINGTONE_OPEN_MODE_MAP = {
{ RINGTONE_FILEMODE_READONLY, O_RDONLY },
{ RINGTONE_FILEMODE_WRITEONLY, O_WRONLY },
{ RINGTONE_FILEMODE_READWRITE, O_RDWR },
{ RINGTONE_FILEMODE_WRITETRUNCATE, O_WRONLY | O_TRUNC },
{ RINGTONE_FILEMODE_WRITEAPPEND, O_WRONLY | O_APPEND },
{ RINGTONE_FILEMODE_READWRITETRUNCATE, O_RDWR | O_TRUNC },
{ RINGTONE_FILEMODE_READWRITEAPPEND, O_RDWR | O_APPEND },
};
if (RINGTONE_OPEN_MODE_MAP.find(mode) == RINGTONE_OPEN_MODE_MAP.end()) {
return E_ERR;
}
if (filePath.size() >= PATH_MAX) {
RINGTONE_ERR_LOG("File path too long %{public}d", (int)filePath.size());
return errCode;
}
string absFilePath;
RingtoneXCollie ringtoneXCollie("VibrateFile PathToRealPath time out",
[](void *) {
RINGTONE_INFO_LOG("VibrateFile PathToRealPath time out");
});
if (!PathToRealPath(filePath, absFilePath)) {
RINGTONE_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
ringtoneXCollie.CancelXCollieTimer();
return errCode;
}
ringtoneXCollie.CancelXCollieTimer();
if (absFilePath.empty()) {
RINGTONE_ERR_LOG("Failed to obtain the canonical path for source path %{public}d %{private}s",
errno, filePath.c_str());
return errCode;
}
RINGTONE_INFO_LOG("File absFilePath is %{private}s", absFilePath.c_str());
return open(absFilePath.c_str(), RINGTONE_OPEN_MODE_MAP.at(mode));
}
bool RingtoneFileUtils::IsFileExists(const string &fileName)
{
struct stat statInfo {};
return ((stat(fileName.c_str(), &statInfo)) == 0);
}
int32_t RingtoneFileUtils::CreateFile(const string &filePath)
{
int32_t errCode = E_ERR;
if (filePath.empty()) {
RINGTONE_ERR_LOG("Filepath is empty");
return E_VIOLATION_PARAMETERS;
}
if (!RingtoneFileUtils::IsTargetExtension(filePath)) {
return E_INVALID_PATH;
}
if (IsFileExists(filePath)) {
RINGTONE_ERR_LOG("the file exists path: %{private}s", filePath.c_str());
return E_FILE_EXIST;
}
ofstream file(filePath);
if (!file) {
RINGTONE_ERR_LOG("Output file path could not be created errno %{public}d", errno);
return errCode;
}
file.close();
return E_SUCCESS;
}
bool RingtoneFileUtils::DeleteFile(const string &fileName)
{
return (remove(fileName.c_str()) == 0);
}
bool RingtoneFileUtils::MoveFile(const string &oldPath, const string &newPath)
{
bool errRet = false;
if (IsFileExists(oldPath) && !IsFileExists(newPath)) {
errRet = (rename(oldPath.c_str(), newPath.c_str()) == 0);
}
return errRet;
}
static bool GetSafeDestPath(const string &newPath, string &absNewPath)
{
bool newPathExists = PathToRealPath(newPath, absNewPath);
if (newPathExists) {
return true;
}
size_t lastSlash = newPath.rfind('/');
if (lastSlash == std::string::npos) {
RINGTONE_ERR_LOG("No parent directory in path");
return false;
}
string parentDir = newPath.substr(0, lastSlash);
string fileName = newPath.substr(lastSlash + 1);
if (fileName.empty() || fileName.find('/') != std::string::npos) {
RINGTONE_ERR_LOG("Invalid file name");
return false;
}
string absParentDir;
if (!PathToRealPath(parentDir, absParentDir)) {
RINGTONE_ERR_LOG("Parent directory not valid");
return false;
}
absNewPath = absParentDir + "/" + fileName;
return true;
}
bool RingtoneFileUtils::CopyFileUtil(const string &filePath, const string &newPath)
{
struct stat fst{};
bool ret = false;
if (filePath.size() >= PATH_MAX) {
RINGTONE_ERR_LOG("File path too long %{public}d", static_cast<int>(filePath.size()));
return ret;
}
RINGTONE_INFO_LOG("File path is %{private}s", filePath.c_str());
string absFilePath;
RingtoneXCollie ringtoneXCollie("CopyFileUtil PathToRealPath time out",
[](void *) {
RINGTONE_INFO_LOG("CopyFileUtil PathToRealPath time out");
});
if (!PathToRealPath(filePath, absFilePath)) {
RINGTONE_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
ringtoneXCollie.CancelXCollieTimer();
return ret;
}
ringtoneXCollie.CancelXCollieTimer();
if (absFilePath.empty()) {
RINGTONE_ERR_LOG("Failed to obtain the canonical path for source path%{private}s %{public}d",
filePath.c_str(), errno);
return ret;
}
int32_t source = open(absFilePath.c_str(), O_RDONLY);
if (source == -1) {
RINGTONE_ERR_LOG("Open failed for source file");
return ret;
}
string absNewPath;
if (!GetSafeDestPath(newPath, absNewPath)) {
close(source);
return false;
}
int32_t dest = open(absNewPath.c_str(), O_WRONLY | O_CREAT, MODE_RW_USR);
if (dest == -1) {
RINGTONE_ERR_LOG("Open failed for destination file %{public}d", errno);
close(source);
return ret;
}
if (fstat(source, &fst) == 0) {
if (sendfile(dest, source, nullptr, fst.st_size) != E_ERR) {
ret = true;
}
}
close(source);
close(dest);
return ret;
}
int32_t RingtoneFileUtils::CopyFileFromFd(int32_t srcFd, const std::string &newPath)
{
CHECK_AND_RETURN_RET_LOG(!newPath.empty(), E_HAS_FS_ERROR,
"file path is empty:%{private}s", newPath.c_str());
std::string filename;
std::string dirPath;
size_t lastSlash = newPath.find_last_of('/');
if (lastSlash != std::string::npos) {
dirPath = newPath.substr(0, lastSlash);
filename = newPath.substr(lastSlash + 1);
} else {
RINGTONE_ERR_LOG("file path is error:%{private}s", newPath.c_str());
return E_HAS_FS_ERROR;
}
char filePathTemp[PATH_MAX] = {0};
bool bflag = realpath(dirPath.c_str(), filePathTemp) == nullptr;
CHECK_AND_RETURN_RET_LOG(!bflag, E_ERR,
"check newPath fail, dirPath = %{private}s", dirPath.c_str());
std::string path = std::string(filePathTemp) + "/" + filename;
CHECK_AND_RETURN_RET_LOG(!RingtoneFileUtils::IsFileExists(path), E_HAS_FS_ERROR,
"error file exists:%{private}s", path.c_str());
int32_t dest = open(path.c_str(), O_WRONLY | O_CREAT, MODE_RW_USR);
CHECK_AND_RETURN_RET_LOG(dest > 0, E_ERR, "Open failed for destination file %{public}d", errno);
OHOS::UniqueFd destFd(dest);
constexpr size_t bufferSize = 4096;
char buffer[bufferSize];
ssize_t bytesRead;
ssize_t bytesWritten;
while ((bytesRead = read(srcFd, buffer, bufferSize)) > 0) {
bytesWritten = write(destFd.Get(), buffer, bytesRead);
if (bytesWritten != bytesRead) {
RINGTONE_ERR_LOG("Failed to copy file from srcFd=%{public}d to destFd=%{public}d, errno=%{public}d",
srcFd, destFd.Get(), errno);
return E_HAS_FS_ERROR;
}
}
if (bytesRead < 0) {
RINGTONE_ERR_LOG("Failed to read from srcFd=%{public}d, errno=%{public}d", srcFd, errno);
return E_HAS_FS_ERROR;
}
return E_OK;
}
bool RingtoneFileUtils::CheckFileSize(int32_t fd, uint64_t maxFileSize)
{
CHECK_AND_RETURN_RET_LOG(fd > 0, false, "invalid fd");
struct stat stat_buf;
CHECK_AND_RETURN_RET_LOG(fstat(fd, &stat_buf) == 0, false, "get fd stat failed");
return static_cast<uint64_t>(stat_buf.st_size) <= maxFileSize;
}
int64_t RingtoneFileUtils::Timespec2Millisecond(const struct timespec &time)
{
return time.tv_sec * MSEC_TO_SEC + time.tv_nsec / MSEC_TO_NSEC;
}
bool RingtoneFileUtils::StartsWith(const string &str, const string &prefix)
{
return str.compare(0, prefix.size(), prefix) == 0;
}
int64_t RingtoneFileUtils::UTCTimeMilliSeconds()
{
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
return t.tv_sec * MSEC_TO_SEC + t.tv_nsec / MSEC_TO_NSEC;
}
int64_t RingtoneFileUtils::UTCTimeSeconds()
{
struct timespec t{};
t.tv_sec = 0;
t.tv_nsec = 0;
clock_gettime(CLOCK_REALTIME, &t);
return (int64_t)(t.tv_sec);
}
string RingtoneFileUtils::StrCreateTimeByMilliseconds(const string &format, int64_t time)
{
if (time <= 0) {
return "";
}
char strTime[DEFAULT_TIME_SIZE] = "";
time_t times = static_cast<time_t>(time / MSEC_TO_SEC);
struct tm tm_info;
tzset();
if (localtime_r(×, &tm_info) == nullptr) {
return "";
}
(void)strftime(strTime, sizeof(strTime), format.c_str(), &tm_info);
return strTime;
}
static const int URL_DECODE_DOUBLE = 2;
string RingtoneFileUtils::UrlDecode(const string &src)
{
string ret;
char ch;
int tmpNum;
for (size_t i = 0; i < src.length(); i++) {
if (src[i]=='%') {
if (sscanf_s(src.substr(i + 1, URL_DECODE_DOUBLE).c_str(), "%x", &tmpNum) == -1) {
RINGTONE_ERR_LOG("Not a valid url: %{private}s", src.c_str());
return src;
}
ch = static_cast<char>(tmpNum);
ret += ch;
i = i + URL_DECODE_DOUBLE;
} else {
ret += src[i];
}
}
return ret;
}
static int32_t MkdirRecursive(const string &path, size_t start)
{
RINGTONE_DEBUG_LOG("start pos %{public}zu", start);
size_t end = path.find("/", start + 1);
string subDir = "";
if (end == std::string::npos) {
if (start + 1 == path.size()) {
RINGTONE_DEBUG_LOG("path size=%zu", path.size());
} else {
subDir = path.substr(start + 1, path.size() - start - 1);
}
} else {
subDir = path.substr(start + 1, end - start - 1);
}
if (subDir.size() == 0) {
return E_SUCCESS;
} else {
string real = path.substr(0, start + subDir.size() + 1);
mode_t mask = umask(0);
int result = mkdir(real.c_str(), MODE_RWX_USR_GRP);
if (result == 0) {
RINGTONE_INFO_LOG("mkdir %{public}s successfully", real.c_str());
} else {
RINGTONE_INFO_LOG("mkdir %{public}s failed, errno is %{public}d", real.c_str(), errno);
}
umask(mask);
}
if (end == std::string::npos) {
return E_SUCCESS;
}
return MkdirRecursive(path, end);
}
int32_t RingtoneFileUtils::CreatePreloadFolder(const string &path)
{
RINGTONE_DEBUG_LOG("start");
if (access(path.c_str(), F_OK) == 0) {
RINGTONE_DEBUG_LOG("dir is existing");
return E_SUCCESS;
}
auto start = path.find(RINGTONE_CUSTOMIZED_BASE_PATH);
if (start == string::npos) {
RINGTONE_ERR_LOG("base dir is wrong");
return E_ERR;
}
return MkdirRecursive(path, start + RINGTONE_CUSTOMIZED_BASE_PATH.size());
}
void RingtoneFileUtils::CreateRingtoneDir()
{
static const vector<string> userPreloadDirs = {
{ RINGTONE_CUSTOMIZED_ALARM_PATH }, { RINGTONE_CUSTOMIZED_RINGTONE_PATH },
{ RINGTONE_CUSTOMIZED_NOTIFICATIONS_PATH }, { RINGTONE_CUSTOMIZED_CONTACTS_PATH },
{ RINGTONE_CUSTOMIZED_APP_NOTIFICATIONS_PATH }
};
for (const auto &dir: userPreloadDirs) {
if (CreatePreloadFolder(dir) != E_SUCCESS) {
RINGTONE_INFO_LOG("scan failed on dir %{public}s", dir.c_str());
continue;
}
}
}
int32_t RingtoneFileUtils::MoveDirectory(const std::string &srcDir, const std::string &dstDir)
{
if (access(srcDir.c_str(), F_OK) != 0) {
RINGTONE_ERR_LOG("access srcDir failed, errno is %{public}d", errno);
return E_FAIL;
}
if (access(dstDir.c_str(), F_OK) != 0) {
RINGTONE_ERR_LOG("access dstDir failed, errno is %{public}d", errno);
return E_FAIL;
}
int ret = E_SUCCESS;
for (const auto &dirEntry : std::filesystem::directory_iterator{ srcDir }) {
std::string srcFilePath = dirEntry.path();
std::string tmpFilePath = srcFilePath;
std::string dstFilePath = tmpFilePath.replace(0, srcDir.length(), dstDir);
if (!MoveFile(srcFilePath, dstFilePath)) {
RINGTONE_ERR_LOG("Move file failed, errno is %{public}d", errno);
ret = E_FAIL;
}
}
return ret;
}
bool CopyRingtoneFolder(const std::filesystem::path& sourcePath, const std::filesystem::path& destinationPath)
{
if (!std::filesystem::exists(sourcePath)) {
RINGTONE_ERR_LOG("source path is not exists");
return false;
}
std::error_code ec;
for (const auto& entry : std::filesystem::directory_iterator(sourcePath, ec)) {
if (ec) {
RINGTONE_ERR_LOG("directory_iterator failed, errno is %{public}d", ec.value());
return false;
}
std::filesystem::path srcPath = entry.path();
std::filesystem::path dstPath = destinationPath / srcPath.filename();
bool isDirectory = std::filesystem::is_directory(srcPath, ec);
if (ec) {
RINGTONE_ERR_LOG("failed to check file type for path: %{public}s, error: %{public}s",
srcPath.c_str(), ec.message().c_str());
return false;
}
if (isDirectory) {
if (!CopyRingtoneFolder(srcPath, dstPath)) {
return false;
}
} else {
if (!RingtoneFileUtils::IsFileExists(srcPath.c_str())) {
RINGTONE_ERR_LOG("file is not exists, path: %{public}s", srcPath.c_str());
return false;
}
if (!RingtoneFileUtils::CopyFileUtil(srcPath.c_str(), dstPath.c_str())) {
RINGTONE_ERR_LOG("copy file failed, dstPath: %{public}s, err: %{public}s",
srcPath.c_str(), strerror(errno));
return false;
}
if (!RingtoneFileUtils::IsFileExists(dstPath.c_str())) {
RINGTONE_ERR_LOG("file is not exists, dstPath : %{public}s, errno is %{public}d",
dstPath.c_str(), errno);
return false;
}
}
}
return true;
}
bool RingtoneFileUtils::MoveRingtoneFolder()
{
const std::filesystem::path oldPath("/storage/media/local/files/Ringtone");
const std::filesystem::path newPath("/data/storage/el2/base/files/Ringtone");
if (!(std::filesystem::exists(RINGTONE_CUSTOMIZED_BASE_PATH))) {
RINGTONE_ERR_LOG("Target path is not exists");
return false;
}
if (!CopyRingtoneFolder(oldPath, newPath)) {
RINGTONE_ERR_LOG("Copy ringtone folder failed");
return false;
}
RINGTONE_INFO_LOG("Ringtone folder move successfully");
return true;
}
void RingtoneFileUtils::CheckAndCreateCustomRingtoneDir()
{
static const vector<string> customDirs = {{ RINGTONE_CUSTOMIZED_CONTACTS_PATH },
{ RINGTONE_CUSTOMIZED_APP_NOTIFICATIONS_PATH }, { RINGTONE_CUSTOMIZED_NOTIFICATIONS_PATH },
{ RINGTONE_CUSTOMIZED_RINGTONE_PATH }, { RINGTONE_CUSTOMIZED_ALARM_PATH }
};
for (const auto &dir: customDirs) {
if (access(dir.c_str(), F_OK) != 0) {
if (CreatePreloadFolder(dir) != E_SUCCESS) {
RINGTONE_ERR_LOG("create %{public}s dir failed!", dir.c_str());
}
}
}
}
void RingtoneFileUtils::AccessRingtoneDir()
{
if (access(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str(), F_OK) != 0) {
CreateRingtoneDir();
return;
}
CheckAndCreateCustomRingtoneDir();
struct stat fileStat;
if (stat(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str(), &fileStat) != 0) {
RINGTONE_ERR_LOG("stat dir failed, errno is %{public}d", errno);
return;
}
if ((fileStat.st_mode & S_IWGRP) != 0) {
return;
}
if (IsEmptyFolder(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str())) {
RINGTONE_ERR_LOG("The directory is empty and lacks group write permission.");
if (DeleteFile(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str())) {
RINGTONE_ERR_LOG("DeleteFile denied, errCode: %{public}d", errno);
}
CreateRingtoneDir();
} else {
if (MoveFile(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str(),
RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str())) {
if (CreatePreloadFolder(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str()) != E_SUCCESS) {
RINGTONE_ERR_LOG("Create Ringtone dir failed, errno is %{public}d", errno);
MoveFile(RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str(),
RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str());
return;
}
if (MoveDirectory(RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str(),
RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str()) != E_SUCCESS) {
RINGTONE_ERR_LOG("Move dir failed, errno is %{public}d", errno);
CreateRingtoneDir();
return;
}
if (DeleteFile(RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str())) {
RINGTONE_ERR_LOG("DeleteFile denied, errCode: %{public}d", errno);
}
} else {
RINGTONE_ERR_LOG("Move Ringtone dir failed, errno is %{public}d", errno);
}
}
return;
}
string RingtoneFileUtils::GetFileExtension(const string &path)
{
if (!path.empty()) {
size_t dotIndex = path.rfind(".");
if (dotIndex != string::npos) {
return path.substr(dotIndex + 1);
}
}
RINGTONE_ERR_LOG("Failed to obtain file extension because given pathname is empty");
return "";
}
void RingtoneFileUtils::RemoveRingtoneFolder(const std::string &path)
{
CHECK_AND_RETURN_LOG(!path.empty(), "Path is empty, cannot remove folder");
if (std::filesystem::exists(path)) {
std::error_code ec;
std::filesystem::remove_all(path, ec);
if (ec) {
RINGTONE_ERR_LOG("remove old ringtone folder failed, errno is %{public}d", ec.value());
RemoveDirectory(path);
return;
}
}
}
}
}