• 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 #define MLOG_TAG "FileUtils"
16 
17 #include "ringtone_file_utils.h"
18 
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <ftw.h>
22 #include <fstream>
23 #include <sstream>
24 #include <sys/sendfile.h>
25 #include <system_error>
26 #include <unistd.h>
27 #include <iostream>
28 
29 #include "directory_ex.h"
30 #include "ringtone_db_const.h"
31 #include "ringtone_errno.h"
32 #include "ringtone_log.h"
33 #include "ringtone_mimetype_utils.h"
34 #include "ringtone_type.h"
35 #include "ringtone_xcollie.h"
36 #include "vibrate_type.h"
37 #include "securec.h"
38 
39 namespace OHOS {
40 namespace Media {
41 using namespace std;
42 static const int32_t OPEN_FDS = 128;
43 static const mode_t MODE_RWX_USR_GRP = 02771;
44 static const mode_t MODE_RW_USR = 0644;
45 const std::string OLD_RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH = "/storage/media/local/files/Ringtone";
46 const vector<string> EXIF_SUPPORTED_EXTENSION = {
47     RINGTONE_CONTAINER_TYPE_3GA,
48     RINGTONE_CONTAINER_TYPE_AC3,
49     RINGTONE_CONTAINER_TYPE_A52,
50     RINGTONE_CONTAINER_TYPE_AMR,
51     RINGTONE_CONTAINER_TYPE_IMY,
52     RINGTONE_CONTAINER_TYPE_RTTTL,
53     RINGTONE_CONTAINER_TYPE_XMF,
54     RINGTONE_CONTAINER_TYPE_RTX,
55     RINGTONE_CONTAINER_TYPE_MXMF,
56     RINGTONE_CONTAINER_TYPE_M4A,
57     RINGTONE_CONTAINER_TYPE_M4B,
58     RINGTONE_CONTAINER_TYPE_M4P,
59     RINGTONE_CONTAINER_TYPE_F4A,
60     RINGTONE_CONTAINER_TYPE_F4B,
61     RINGTONE_CONTAINER_TYPE_F4P,
62     RINGTONE_CONTAINER_TYPE_M3U,
63     RINGTONE_CONTAINER_TYPE_SMF,
64     RINGTONE_CONTAINER_TYPE_MKA,
65     RINGTONE_CONTAINER_TYPE_RA,
66     RINGTONE_CONTAINER_TYPE_MP3,
67     RINGTONE_CONTAINER_TYPE_AAC,
68     RINGTONE_CONTAINER_TYPE_ADTS,
69     RINGTONE_CONTAINER_TYPE_ADT,
70     RINGTONE_CONTAINER_TYPE_SND,
71     RINGTONE_CONTAINER_TYPE_FLAC,
72     RINGTONE_CONTAINER_TYPE_MP2,
73     RINGTONE_CONTAINER_TYPE_MP1,
74     RINGTONE_CONTAINER_TYPE_MPA,
75     RINGTONE_CONTAINER_TYPE_M4R,
76     RINGTONE_CONTAINER_TYPE_WAV,
77     RINGTONE_CONTAINER_TYPE_OGG,
78     RINGTONE_CONTAINER_TYPE_VIDEO_MP4
79 };
80 
IsTargetExtension(const string & path)81 static bool IsTargetExtension(const string &path)
82 {
83     const string ext = RingtoneFileUtils::GetExtensionFromPath(path);
84     std::string mimeType = RingtoneMimeTypeUtils::GetMimeTypeFromExtension(ext);
85     int32_t mime = RingtoneMimeTypeUtils::GetMediaTypeFromMimeType(mimeType);
86     if (mime == RINGTONE_MEDIA_TYPE_AUDIO) {
87         return true;
88     } else if (mime == RINGTONE_MEDIA_TYPE_VIDEO) {
89         return true;
90     }
91     RINGTONE_ERR_LOG("MimeType error:%{public}s,%{public}s", ext.c_str(), mimeType.c_str());
92     bool ret = find(EXIF_SUPPORTED_EXTENSION.begin(), EXIF_SUPPORTED_EXTENSION.end(), ext) !=
93         EXIF_SUPPORTED_EXTENSION.end();
94     if (!ret) {
95         RINGTONE_ERR_LOG("invalid target extension:%{public}s", ext.c_str());
96     }
97     return ret;
98 }
99 
IsVibrateFile(const string & path)100 static bool IsVibrateFile(const string &path)
101 {
102     const string ext = RingtoneFileUtils::GetExtensionFromPath(path);
103     bool ret = (ext == VIBRATE_CONTAINER_TYPE_JSON);
104     if (!ret) {
105         RINGTONE_ERR_LOG("invalid target extension:%{public}s", ext.c_str());
106     }
107     return ret;
108 }
109 
SplitByChar(const string & str,const char split)110 string RingtoneFileUtils::SplitByChar(const string &str, const char split)
111 {
112     size_t splitIndex = str.find_last_of(split);
113     return (splitIndex == string::npos) ? ("") : (str.substr(splitIndex + 1));
114 }
115 
GetExtensionFromPath(const string & path)116 string RingtoneFileUtils::GetExtensionFromPath(const string &path)
117 {
118     string extention = SplitByChar(path, '.');
119     if (!extention.empty()) {
120         transform(extention.begin(), extention.end(), extention.begin(), ::tolower);
121     }
122     return extention;
123 }
124 
GetFileNameFromPath(const string & path)125 string RingtoneFileUtils::GetFileNameFromPath(const string &path)
126 {
127     string fileName = {};
128     size_t found = path.rfind("/");
129     if (found != string::npos && (found + 1) < path.size()) {
130         fileName = path.substr(found + 1);
131     } else {
132         fileName = "";
133     }
134 
135     return fileName;
136 }
137 
ParseFromUri(const string & path,const string & key)138 static string ParseFromUri(const string& path, const string& key)
139 {
140     RINGTONE_INFO_LOG("parsing uri : %{public}s for key : %{public}s", path.c_str(), key.c_str());
141     auto keyLen = key.size();
142     auto found = path.find(key);
143     if (found == string::npos) {
144         RINGTONE_INFO_LOG("there is no such field in uri: %{public}s", path.c_str());
145         return "";
146     }
147     string sub = path.substr(found + keyLen + 1);
148     found = sub.find("&");
149     if (found != string::npos) {
150         sub = sub.substr(0, found);
151     }
152     sub = RingtoneFileUtils::UrlDecode(sub);
153     RINGTONE_INFO_LOG("parsing uri : %{public}s -> key=%{public}s, value=%{public}s",
154         path.c_str(), key.c_str(), sub.c_str());
155     return sub;
156 }
157 
GetFileNameFromPathOrUri(const string & path,bool & isTitle)158 string RingtoneFileUtils::GetFileNameFromPathOrUri(const string &path, bool &isTitle)
159 {
160     string fileName = {};
161     size_t found = path.find("content://");
162     if (found == 0) {
163         fileName = ParseFromUri(path, "title"); // Pay attention! It's actually "title".
164         isTitle = true;
165     } else {
166         fileName = GetFileNameFromPath(path);
167         isTitle = false;
168     }
169     RINGTONE_INFO_LOG("%{public}s -> %{public}s", path.c_str(), fileName.c_str());
170     return fileName;
171 }
172 
GetBaseNameFromPath(const string & path)173 string RingtoneFileUtils::GetBaseNameFromPath(const string &path)
174 {
175     size_t found = path.rfind("/");
176     size_t foundDot = path.rfind(".");
177 
178     string baseName = {};
179     found = (found == string::npos ? 0 : found);
180     if ((foundDot > found) && (foundDot != string::npos)) {
181         baseName = path.substr(found + 1, foundDot - found - 1);
182     } else {
183         baseName = "";
184     }
185 
186     return baseName;
187 }
188 
IsSameFile(const string & srcPath,const string & dstPath)189 bool RingtoneFileUtils::IsSameFile(const string &srcPath, const string &dstPath)
190 {
191     struct stat srcStatInfo {};
192     struct stat dstStatInfo {};
193 
194     if (access(srcPath.c_str(), F_OK) || access(dstPath.c_str(), F_OK)) {
195         return false;
196     }
197     if (stat(srcPath.c_str(), &srcStatInfo) != 0) {
198         RINGTONE_ERR_LOG("Failed to get file %{private}s StatInfo, err=%{public}d", srcPath.c_str(), errno);
199         return false;
200     }
201     if (stat(dstPath.c_str(), &dstStatInfo) != 0) {
202         RINGTONE_ERR_LOG("Failed to get file %{private}s StatInfo, err=%{public}d", dstPath.c_str(), errno);
203         return false;
204     }
205     if (srcStatInfo.st_size != dstStatInfo.st_size) { /* file size */
206         RINGTONE_INFO_LOG("Size differs, srcStatInfo.st_size != dstStatInfo.st_size");
207         return false;
208     }
209 
210     return true;
211 }
212 
UnlinkCb(const char * fpath,const struct stat * sb,int32_t typeflag,struct FTW * ftwbuf)213 static int32_t UnlinkCb(const char *fpath, const struct stat *sb, int32_t typeflag, struct FTW *ftwbuf)
214 {
215     CHECK_AND_RETURN_RET_LOG(fpath != nullptr, E_FAIL, "fpath == nullptr");
216     int32_t errRet = remove(fpath);
217     if (errRet) {
218         RINGTONE_ERR_LOG("Failed to remove errno: %{public}d, path: %{private}s", errno, fpath);
219     }
220 
221     return errRet;
222 }
223 
RemoveDirectory(const string & path)224 int32_t RingtoneFileUtils::RemoveDirectory(const string &path)
225 {
226     return nftw(path.c_str(), UnlinkCb, OPEN_FDS, FTW_DEPTH | FTW_PHYS);
227 }
228 
Mkdir(const string & subStr,shared_ptr<int> errCodePtr)229 bool RingtoneFileUtils::Mkdir(const string &subStr, shared_ptr<int> errCodePtr)
230 {
231     mode_t mask = umask(0);
232     if (mkdir(subStr.c_str(), MODE_RWX_USR_GRP) == -1) {
233         if (errCodePtr != nullptr) {
234             *errCodePtr = errno;
235         }
236         RINGTONE_ERR_LOG("Failed to create directory %{public}d", errno);
237         umask(mask);
238         return (errno == EEXIST) ? true : false;
239     }
240     umask(mask);
241     return true;
242 }
243 
IsDirectory(const string & dirName,shared_ptr<int> errCodePtr)244 bool RingtoneFileUtils::IsDirectory(const string &dirName, shared_ptr<int> errCodePtr)
245 {
246     struct stat statInfo {};
247 
248     if (stat(dirName.c_str(), &statInfo) == 0) {
249         if (statInfo.st_mode & S_IFDIR) {
250             return true;
251         }
252     } else if (errCodePtr != nullptr) {
253         *errCodePtr = errno;
254         return false;
255     }
256 
257     return false;
258 }
259 
CreateDirectory(const string & dirPath,shared_ptr<int> errCodePtr)260 bool RingtoneFileUtils::CreateDirectory(const string &dirPath, shared_ptr<int> errCodePtr)
261 {
262     string subStr;
263     string segment;
264 
265     stringstream folderStream(dirPath);
266     while (getline(folderStream, segment, '/')) {
267         if (segment.empty()) {
268             continue;
269         }
270 
271         subStr.append(RINGTONE_SLASH_CHAR + segment);
272         if (!IsDirectory(subStr, errCodePtr)) {
273             if (!Mkdir(subStr, errCodePtr)) {
274                 return false;
275             }
276         }
277     }
278     return true;
279 }
280 
OpenFile(const string & filePath,const string & mode)281 int32_t RingtoneFileUtils::OpenFile(const string &filePath, const string &mode)
282 {
283     int32_t errCode = E_ERR;
284 
285     if (filePath.empty() || mode.empty()) {
286         RINGTONE_ERR_LOG("Invalid open argument! mode: %{private}s, path: %{private}s", mode.c_str(), filePath.c_str());
287         return errCode;
288     }
289 
290     if (!IsTargetExtension(filePath)) {
291         return E_INVALID_PATH;
292     }
293 
294     static const unordered_map<string, int32_t> RINGTONE_OPEN_MODE_MAP = {
295         { RINGTONE_FILEMODE_READONLY, O_RDONLY },
296         { RINGTONE_FILEMODE_WRITEONLY, O_WRONLY },
297         { RINGTONE_FILEMODE_READWRITE, O_RDWR },
298         { RINGTONE_FILEMODE_WRITETRUNCATE, O_WRONLY | O_TRUNC },
299         { RINGTONE_FILEMODE_WRITEAPPEND, O_WRONLY | O_APPEND },
300         { RINGTONE_FILEMODE_READWRITETRUNCATE, O_RDWR | O_TRUNC },
301         { RINGTONE_FILEMODE_READWRITEAPPEND, O_RDWR | O_APPEND },
302     };
303     if (RINGTONE_OPEN_MODE_MAP.find(mode) == RINGTONE_OPEN_MODE_MAP.end()) {
304         return E_ERR;
305     }
306 
307     if (filePath.size() >= PATH_MAX) {
308         RINGTONE_ERR_LOG("File path too long %{public}d", (int)filePath.size());
309         return errCode;
310     }
311     string absFilePath;
312     RingtoneXCollie ringtoneXCollie("PathToRealPath time out",
313         [](void *) {
314             RINGTONE_INFO_LOG("PathToRealPath time out");
315         });
316     if (!PathToRealPath(filePath, absFilePath)) {
317         RINGTONE_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
318         ringtoneXCollie.CancelXCollieTimer();
319         return errCode;
320     }
321     ringtoneXCollie.CancelXCollieTimer();
322     if (absFilePath.empty()) {
323         RINGTONE_ERR_LOG("Failed to obtain the canonical path for source path %{public}d %{private}s",
324             errno, filePath.c_str());
325         return errCode;
326     }
327     RINGTONE_INFO_LOG("File absFilePath is %{private}s", absFilePath.c_str());
328     return open(absFilePath.c_str(), RINGTONE_OPEN_MODE_MAP.at(mode));
329 }
330 
OpenVibrateFile(const std::string & filePath,const std::string & mode)331 int32_t RingtoneFileUtils::OpenVibrateFile(const std::string &filePath, const std::string &mode)
332 {
333     int32_t errCode = E_ERR;
334 
335     if (filePath.empty() || mode.empty()) {
336         RINGTONE_ERR_LOG("Invalid open argument! mode: %{private}s, path: %{private}s", mode.c_str(), filePath.c_str());
337         return errCode;
338     }
339 
340     if (!IsVibrateFile(filePath)) {
341         return E_INVALID_PATH;
342     }
343 
344     static const unordered_map<string, int32_t> RINGTONE_OPEN_MODE_MAP = {
345         { RINGTONE_FILEMODE_READONLY, O_RDONLY },
346         { RINGTONE_FILEMODE_WRITEONLY, O_WRONLY },
347         { RINGTONE_FILEMODE_READWRITE, O_RDWR },
348         { RINGTONE_FILEMODE_WRITETRUNCATE, O_WRONLY | O_TRUNC },
349         { RINGTONE_FILEMODE_WRITEAPPEND, O_WRONLY | O_APPEND },
350         { RINGTONE_FILEMODE_READWRITETRUNCATE, O_RDWR | O_TRUNC },
351         { RINGTONE_FILEMODE_READWRITEAPPEND, O_RDWR | O_APPEND },
352     };
353     if (RINGTONE_OPEN_MODE_MAP.find(mode) == RINGTONE_OPEN_MODE_MAP.end()) {
354         return E_ERR;
355     }
356 
357     if (filePath.size() >= PATH_MAX) {
358         RINGTONE_ERR_LOG("File path too long %{public}d", (int)filePath.size());
359         return errCode;
360     }
361     string absFilePath;
362     RingtoneXCollie ringtoneXCollie("VibrateFile PathToRealPath time out",
363         [](void *) {
364             RINGTONE_INFO_LOG("VibrateFile PathToRealPath time out");
365         });
366     if (!PathToRealPath(filePath, absFilePath)) {
367         RINGTONE_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
368         ringtoneXCollie.CancelXCollieTimer();
369         return errCode;
370     }
371     ringtoneXCollie.CancelXCollieTimer();
372     if (absFilePath.empty()) {
373         RINGTONE_ERR_LOG("Failed to obtain the canonical path for source path %{public}d %{private}s",
374             errno, filePath.c_str());
375         return errCode;
376     }
377     RINGTONE_INFO_LOG("File absFilePath is %{private}s", absFilePath.c_str());
378     return open(absFilePath.c_str(), RINGTONE_OPEN_MODE_MAP.at(mode));
379 }
380 
IsFileExists(const string & fileName)381 bool RingtoneFileUtils::IsFileExists(const string &fileName)
382 {
383     struct stat statInfo {};
384 
385     return ((stat(fileName.c_str(), &statInfo)) == 0);
386 }
387 
CreateFile(const string & filePath)388 int32_t RingtoneFileUtils::CreateFile(const string &filePath)
389 {
390     int32_t errCode = E_ERR;
391 
392     if (filePath.empty()) {
393         RINGTONE_ERR_LOG("Filepath is empty");
394         return E_VIOLATION_PARAMETERS;
395     }
396 
397     if (!IsTargetExtension(filePath)) {
398         return E_INVALID_PATH;
399     }
400 
401     if (IsFileExists(filePath)) {
402         RINGTONE_ERR_LOG("the file exists path: %{private}s", filePath.c_str());
403         return E_FILE_EXIST;
404     }
405 
406     ofstream file(filePath);
407     if (!file) {
408         RINGTONE_ERR_LOG("Output file path could not be created errno %{public}d", errno);
409         return errCode;
410     }
411 
412     file.close();
413 
414     return E_SUCCESS;
415 }
416 
DeleteFile(const string & fileName)417 bool RingtoneFileUtils::DeleteFile(const string &fileName)
418 {
419     return (remove(fileName.c_str()) == 0);
420 }
421 
MoveFile(const string & oldPath,const string & newPath)422 bool RingtoneFileUtils::MoveFile(const string &oldPath, const string &newPath)
423 {
424     bool errRet = false;
425 
426     if (IsFileExists(oldPath) && !IsFileExists(newPath)) {
427         errRet = (rename(oldPath.c_str(), newPath.c_str()) == 0);
428     }
429 
430     return errRet;
431 }
432 
CopyFileUtil(const string & filePath,const string & newPath)433 bool RingtoneFileUtils::CopyFileUtil(const string &filePath, const string &newPath)
434 {
435     struct stat fst{};
436     bool ret = false;
437     if (filePath.size() >= PATH_MAX) {
438         RINGTONE_ERR_LOG("File path too long %{public}d", static_cast<int>(filePath.size()));
439         return ret;
440     }
441     RINGTONE_INFO_LOG("File path is %{private}s", filePath.c_str());
442     string absFilePath;
443     RingtoneXCollie ringtoneXCollie("CopyFileUtil PathToRealPath time out",
444         [](void *) {
445             RINGTONE_INFO_LOG("CopyFileUtil PathToRealPath time out");
446         });
447     if (!PathToRealPath(filePath, absFilePath)) {
448         RINGTONE_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
449         ringtoneXCollie.CancelXCollieTimer();
450         return ret;
451     }
452     ringtoneXCollie.CancelXCollieTimer();
453     if (absFilePath.empty()) {
454         RINGTONE_ERR_LOG("Failed to obtain the canonical path for source path%{private}s %{public}d",
455             filePath.c_str(), errno);
456         return ret;
457     }
458 
459     int32_t source = open(absFilePath.c_str(), O_RDONLY);
460     if (source == -1) {
461         RINGTONE_ERR_LOG("Open failed for source file");
462         return ret;
463     }
464 
465     int32_t dest = open(newPath.c_str(), O_WRONLY | O_CREAT, MODE_RW_USR);
466     if (dest == -1) {
467         RINGTONE_ERR_LOG("Open failed for destination file %{public}d", errno);
468         close(source);
469         return ret;
470     }
471 
472     if (fstat(source, &fst) == 0) {
473         // Copy file content
474         if (sendfile(dest, source, nullptr, fst.st_size) != E_ERR) {
475             ret = true;
476         }
477     }
478 
479     close(source);
480     close(dest);
481 
482     return ret;
483 }
484 
Timespec2Millisecond(const struct timespec & time)485 int64_t RingtoneFileUtils::Timespec2Millisecond(const struct timespec &time)
486 {
487     return time.tv_sec * MSEC_TO_SEC + time.tv_nsec / MSEC_TO_NSEC;
488 }
489 
StartsWith(const string & str,const string & prefix)490 bool RingtoneFileUtils::StartsWith(const string &str, const string &prefix)
491 {
492     return str.compare(0, prefix.size(), prefix) == 0;
493 }
494 
UTCTimeMilliSeconds()495 int64_t RingtoneFileUtils::UTCTimeMilliSeconds()
496 {
497     struct timespec t;
498     clock_gettime(CLOCK_REALTIME, &t);
499     return t.tv_sec * MSEC_TO_SEC + t.tv_nsec / MSEC_TO_NSEC;
500 }
501 
UTCTimeSeconds()502 int64_t RingtoneFileUtils::UTCTimeSeconds()
503 {
504     struct timespec t{};
505     t.tv_sec = 0;
506     t.tv_nsec = 0;
507     clock_gettime(CLOCK_REALTIME, &t);
508     return (int64_t)(t.tv_sec);
509 }
510 
StrCreateTimeByMilliseconds(const string & format,int64_t time)511 string RingtoneFileUtils::StrCreateTimeByMilliseconds(const string &format, int64_t time)
512 {
513     char strTime[DEFAULT_TIME_SIZE] = "";
514     int64_t times = time / MSEC_TO_SEC;
515     auto tm = localtime(&times);
516     if (tm == nullptr) {
517         return "";
518     }
519     (void)strftime(strTime, sizeof(strTime), format.c_str(), tm);
520     return strTime;
521 }
522 
523 static const int URL_DECODE_DOUBLE = 2;
UrlDecode(const string & src)524 string RingtoneFileUtils::UrlDecode(const string &src)
525 {
526     string ret;
527     char ch;
528     int tmpNum;
529     for (size_t i = 0; i < src.length(); i++) {
530         if (src[i]=='%') {
531             if (sscanf_s(src.substr(i + 1, URL_DECODE_DOUBLE).c_str(), "%x", &tmpNum) == -1) {
532                 RINGTONE_ERR_LOG("Not a valid url: %{private}s", src.c_str());
533                 return src;
534             }
535             ch = static_cast<char>(tmpNum);
536             ret += ch;
537             i = i + URL_DECODE_DOUBLE;
538         } else {
539             ret += src[i];
540         }
541     }
542     return ret;
543 }
544 
MkdirRecursive(const string & path,size_t start)545 static int32_t MkdirRecursive(const string &path, size_t start)
546 {
547     RINGTONE_DEBUG_LOG("start pos %{public}zu", start);
548     size_t end = path.find("/", start + 1);
549 
550     string subDir = "";
551     if (end == std::string::npos) {
552         if (start + 1 == path.size()) {
553             RINGTONE_DEBUG_LOG("path size=%zu", path.size());
554         } else {
555             subDir = path.substr(start + 1, path.size() - start - 1);
556         }
557     } else {
558         subDir = path.substr(start + 1, end - start - 1);
559     }
560 
561     if (subDir.size() == 0) {
562         return E_SUCCESS;
563     } else {
564         string real = path.substr(0, start + subDir.size() + 1);
565         mode_t mask = umask(0);
566         int result = mkdir(real.c_str(), MODE_RWX_USR_GRP);
567         if (result == 0) {
568             RINGTONE_INFO_LOG("mkdir %{public}s successfully", real.c_str());
569         } else {
570             RINGTONE_INFO_LOG("mkdir %{public}s failed, errno is %{public}d", real.c_str(), errno);
571         }
572         umask(mask);
573     }
574     if (end == std::string::npos) {
575         return E_SUCCESS;
576     }
577 
578     return MkdirRecursive(path, end);
579 }
580 
CreatePreloadFolder(const string & path)581 int32_t RingtoneFileUtils::CreatePreloadFolder(const string &path)
582 {
583     RINGTONE_DEBUG_LOG("start");
584     if (access(path.c_str(), F_OK) == 0) {
585         RINGTONE_DEBUG_LOG("dir is existing");
586         return E_SUCCESS;
587     }
588 
589     auto start = path.find(RINGTONE_CUSTOMIZED_BASE_PATH);
590     if (start == string::npos) {
591         RINGTONE_ERR_LOG("base dir is wrong");
592         return E_ERR;
593     }
594 
595     return MkdirRecursive(path, start + RINGTONE_CUSTOMIZED_BASE_PATH.size());
596 }
597 
CreateRingtoneDir()598 void RingtoneFileUtils::CreateRingtoneDir()
599 {
600     static const vector<string> userPreloadDirs = {
601         { RINGTONE_CUSTOMIZED_ALARM_PATH }, { RINGTONE_CUSTOMIZED_RINGTONE_PATH },
602         { RINGTONE_CUSTOMIZED_NOTIFICATIONS_PATH }, { RINGTONE_CUSTOMIZED_CONTACTS_PATH }
603     };
604 
605     for (const auto &dir: userPreloadDirs) {
606         if (CreatePreloadFolder(dir) != E_SUCCESS) {
607             RINGTONE_INFO_LOG("scan failed on dir %{public}s", dir.c_str());
608             continue;
609         }
610     }
611 }
612 
MoveDirectory(const std::string & srcDir,const std::string & dstDir)613 int32_t RingtoneFileUtils::MoveDirectory(const std::string &srcDir, const std::string &dstDir)
614 {
615     if (access(srcDir.c_str(), F_OK) != 0) {
616         RINGTONE_ERR_LOG("access srcDir failed, errno is %{public}d", errno);
617         return E_FAIL;
618     }
619     if (access(dstDir.c_str(), F_OK) != 0) {
620         RINGTONE_ERR_LOG("access dstDir failed, errno is %{public}d", errno);
621         return E_FAIL;
622     }
623     int ret = E_SUCCESS;
624     for (const auto &dirEntry : std::filesystem::directory_iterator{ srcDir }) {
625         std::string srcFilePath = dirEntry.path();
626         std::string tmpFilePath = srcFilePath;
627         std::string dstFilePath = tmpFilePath.replace(0, srcDir.length(), dstDir);
628         if (!MoveFile(srcFilePath, dstFilePath)) {
629             RINGTONE_ERR_LOG("Move file failed, errno is %{public}d", errno);
630             ret = E_FAIL;
631         }
632     }
633     return ret;
634 }
CopyRingtoneFolder(const std::filesystem::path & sourcePath,const std::filesystem::path & destinationPath)635 bool CopyRingtoneFolder(const std::filesystem::path& sourcePath, const std::filesystem::path& destinationPath)
636 {
637     if (!std::filesystem::exists(sourcePath)) {
638         RINGTONE_ERR_LOG("source path is not exists, errno is %{public}d", errno);
639         return false;
640     }
641     for (const auto& entry : std::filesystem::directory_iterator(sourcePath)) {
642         std::filesystem::path srcPath = entry.path();
643         std::filesystem::path dstPath = destinationPath / srcPath.filename();
644 
645         if (std::filesystem::is_directory(srcPath)) {
646             if (!CopyRingtoneFolder(srcPath, dstPath)) {
647                 return false;
648             }
649         } else {
650             std::filesystem::copy_file(srcPath, dstPath, std::filesystem::copy_options::overwrite_existing);
651             if (!std::filesystem::exists(dstPath)) {
652                 RINGTONE_ERR_LOG("copy file failed, dstPath : %{public}s, errno is %{public}d", dstPath.c_str(), errno);
653                 return false;
654             }
655         }
656     }
657     return true;
658 }
659 
MoveRingtoneFolder()660 bool RingtoneFileUtils::MoveRingtoneFolder()
661 {
662     const std::filesystem::path oldPath("/storage/media/local/files/Ringtone");
663     const std::filesystem::path newPath("/data/storage/el2/base/files/Ringtone");
664     if (!(std::filesystem::exists(RINGTONE_CUSTOMIZED_BASE_PATH))) {
665         RINGTONE_ERR_LOG("Target path is not exists");
666         return false;
667     }
668     if (!CopyRingtoneFolder(oldPath, newPath)) {
669         RINGTONE_ERR_LOG("Copy ringtone folder failed");
670         return false;
671     }
672     RINGTONE_INFO_LOG("Ringtone folder move successfully");
673     return true;
674 }
675 
AccessRingtoneDir()676 void RingtoneFileUtils::AccessRingtoneDir()
677 {
678     if (access(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str(), F_OK) != 0) {
679         CreateRingtoneDir();
680         return;
681     }
682 
683     // 检查contacts目录是否创建
684     if (access(RINGTONE_CUSTOMIZED_CONTACTS_PATH.c_str(), F_OK) != 0) {
685         if (CreatePreloadFolder(RINGTONE_CUSTOMIZED_CONTACTS_PATH) != E_SUCCESS) {
686             RINGTONE_ERR_LOG("create contacts dir failed!");
687         }
688     }
689 
690     struct stat fileStat;
691     if (stat(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str(), &fileStat) != 0) {
692         RINGTONE_ERR_LOG("stat dir failed, errno is %{public}d", errno);
693         return;
694     }
695     // 检查组的写权限
696     if ((fileStat.st_mode & S_IWGRP) != 0) {
697         return;
698     }
699     if (IsEmptyFolder(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str())) {
700         RINGTONE_ERR_LOG("The directory is empty and lacks group write permission.");
701         if (DeleteFile(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str())) {
702             RINGTONE_ERR_LOG("DeleteFile denied, errCode: %{public}d", errno);
703         }
704         CreateRingtoneDir();
705     } else { //rename and move file
706         if (MoveFile(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str(),
707             RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str())) {
708             if (CreatePreloadFolder(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str()) != E_SUCCESS) {
709                 RINGTONE_ERR_LOG("Create Ringtone dir failed, errno is %{public}d", errno);
710                 //restore dir
711                 MoveFile(RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str(),
712                     RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str());
713                 return;
714             }
715             if (MoveDirectory(RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str(),
716                 RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str()) != E_SUCCESS) {
717                 RINGTONE_ERR_LOG("Move dir failed, errno is %{public}d", errno);
718                 CreateRingtoneDir();
719                 return;
720             }
721             if (DeleteFile(RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str())) {
722                 RINGTONE_ERR_LOG("DeleteFile denied, errCode: %{public}d", errno);
723             }
724         } else {
725             RINGTONE_ERR_LOG("Move Ringtone dir failed, errno is %{public}d", errno);
726         }
727     }
728     return;
729 }
730 
GetFileExtension(const string & path)731 string RingtoneFileUtils::GetFileExtension(const string &path)
732 {
733     if (!path.empty()) {
734         size_t dotIndex = path.rfind(".");
735         if (dotIndex != string::npos) {
736             return path.substr(dotIndex + 1);
737         }
738     }
739 
740     RINGTONE_ERR_LOG("Failed to obtain file extension because given pathname is empty");
741     return "";
742 }
743 
RemoveRingtoneFolder(const std::string & path)744 void RingtoneFileUtils::RemoveRingtoneFolder(const std::string &path)
745 {
746     CHECK_AND_RETURN_LOG(!path.empty(), "Path is empty, cannot remove folder");
747     if (std::filesystem::exists(path)) {
748         std::error_code ec;
749         std::filesystem::remove_all(path, ec);
750         if (ec) {
751             RINGTONE_ERR_LOG("remove old ringtone folder failed, errno is %{public}d", ec.value());
752             return;
753         }
754     }
755 }
756 } // namespace Media
757 } // namespace OHOS
758