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 <unistd.h>
26 #include <iostream>
27
28 #include "directory_ex.h"
29 #include "ringtone_db_const.h"
30 #include "ringtone_errno.h"
31 #include "ringtone_log.h"
32 #include "ringtone_mimetype_utils.h"
33 #include "ringtone_type.h"
34 #include "ringtone_xcollie.h"
35 #include "vibrate_type.h"
36 #include "securec.h"
37
38 namespace OHOS {
39 namespace Media {
40 using namespace std;
41 static const int32_t OPEN_FDS = 128;
42 static const mode_t MODE_RWX_USR_GRP = 02771;
43 static const mode_t MODE_RW_USR = 0644;
44 const std::string OLD_RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH = "/storage/media/local/files/Ringtone";
45 const vector<string> EXIF_SUPPORTED_EXTENSION = {
46 RINGTONE_CONTAINER_TYPE_3GA,
47 RINGTONE_CONTAINER_TYPE_AC3,
48 RINGTONE_CONTAINER_TYPE_A52,
49 RINGTONE_CONTAINER_TYPE_AMR,
50 RINGTONE_CONTAINER_TYPE_IMY,
51 RINGTONE_CONTAINER_TYPE_RTTTL,
52 RINGTONE_CONTAINER_TYPE_XMF,
53 RINGTONE_CONTAINER_TYPE_RTX,
54 RINGTONE_CONTAINER_TYPE_MXMF,
55 RINGTONE_CONTAINER_TYPE_M4A,
56 RINGTONE_CONTAINER_TYPE_M4B,
57 RINGTONE_CONTAINER_TYPE_M4P,
58 RINGTONE_CONTAINER_TYPE_F4A,
59 RINGTONE_CONTAINER_TYPE_F4B,
60 RINGTONE_CONTAINER_TYPE_F4P,
61 RINGTONE_CONTAINER_TYPE_M3U,
62 RINGTONE_CONTAINER_TYPE_SMF,
63 RINGTONE_CONTAINER_TYPE_MKA,
64 RINGTONE_CONTAINER_TYPE_RA,
65 RINGTONE_CONTAINER_TYPE_MP3,
66 RINGTONE_CONTAINER_TYPE_AAC,
67 RINGTONE_CONTAINER_TYPE_ADTS,
68 RINGTONE_CONTAINER_TYPE_ADT,
69 RINGTONE_CONTAINER_TYPE_SND,
70 RINGTONE_CONTAINER_TYPE_FLAC,
71 RINGTONE_CONTAINER_TYPE_MP2,
72 RINGTONE_CONTAINER_TYPE_MP1,
73 RINGTONE_CONTAINER_TYPE_MPA,
74 RINGTONE_CONTAINER_TYPE_M4R,
75 RINGTONE_CONTAINER_TYPE_WAV,
76 RINGTONE_CONTAINER_TYPE_OGG
77 };
78
IsTargetExtension(const string & path)79 static bool IsTargetExtension(const string &path)
80 {
81 const string ext = RingtoneFileUtils::GetExtensionFromPath(path);
82 std::string mimeType = RingtoneMimeTypeUtils::GetMimeTypeFromExtension(ext);
83 int32_t mime = RingtoneMimeTypeUtils::GetMediaTypeFromMimeType(mimeType);
84 if (mime == RINGTONE_MEDIA_TYPE_AUDIO) {
85 return true;
86 }
87 RINGTONE_ERR_LOG("MimeType error:%{public}s,%{public}s", ext.c_str(), mimeType.c_str());
88 bool ret = find(EXIF_SUPPORTED_EXTENSION.begin(), EXIF_SUPPORTED_EXTENSION.end(), ext) !=
89 EXIF_SUPPORTED_EXTENSION.end();
90 if (!ret) {
91 RINGTONE_ERR_LOG("invalid target extension:%{public}s", ext.c_str());
92 }
93 return ret;
94 }
95
IsVibrateFile(const string & path)96 static bool IsVibrateFile(const string &path)
97 {
98 const string ext = RingtoneFileUtils::GetExtensionFromPath(path);
99 bool ret = (ext == VIBRATE_CONTAINER_TYPE_JSON);
100 if (!ret) {
101 RINGTONE_ERR_LOG("invalid target extension:%{public}s", ext.c_str());
102 }
103 return ret;
104 }
105
SplitByChar(const string & str,const char split)106 string RingtoneFileUtils::SplitByChar(const string &str, const char split)
107 {
108 size_t splitIndex = str.find_last_of(split);
109 return (splitIndex == string::npos) ? ("") : (str.substr(splitIndex + 1));
110 }
111
GetExtensionFromPath(const string & path)112 string RingtoneFileUtils::GetExtensionFromPath(const string &path)
113 {
114 string extention = SplitByChar(path, '.');
115 if (!extention.empty()) {
116 transform(extention.begin(), extention.end(), extention.begin(), ::tolower);
117 }
118 return extention;
119 }
120
GetFileNameFromPath(const string & path)121 string RingtoneFileUtils::GetFileNameFromPath(const string &path)
122 {
123 string fileName = {};
124 size_t found = path.rfind("/");
125 if (found != string::npos && (found + 1) < path.size()) {
126 fileName = path.substr(found + 1);
127 } else {
128 fileName = "";
129 }
130
131 return fileName;
132 }
133
ParseFromUri(const string & path,const string & key)134 static string ParseFromUri(const string& path, const string& key)
135 {
136 RINGTONE_INFO_LOG("parsing uri : %{public}s for key : %{public}s", path.c_str(), key.c_str());
137 auto keyLen = key.size();
138 auto found = path.find(key);
139 if (found == string::npos) {
140 RINGTONE_INFO_LOG("there is no such field in uri: %{public}s", path.c_str());
141 return "";
142 }
143 string sub = path.substr(found + keyLen + 1);
144 found = sub.find("&");
145 if (found != string::npos) {
146 sub = sub.substr(0, found);
147 }
148 sub = RingtoneFileUtils::UrlDecode(sub);
149 RINGTONE_INFO_LOG("parsing uri : %{public}s -> key=%{public}s, value=%{public}s",
150 path.c_str(), key.c_str(), sub.c_str());
151 return sub;
152 }
153
GetFileNameFromPathOrUri(const string & path,bool & isTitle)154 string RingtoneFileUtils::GetFileNameFromPathOrUri(const string &path, bool &isTitle)
155 {
156 string fileName = {};
157 size_t found = path.find("content://");
158 if (found == 0) {
159 fileName = ParseFromUri(path, "title"); // Pay attention! It's actually "title".
160 isTitle = true;
161 } else {
162 fileName = GetFileNameFromPath(path);
163 isTitle = false;
164 }
165 RINGTONE_INFO_LOG("%{public}s -> %{public}s", path.c_str(), fileName.c_str());
166 return fileName;
167 }
168
GetBaseNameFromPath(const string & path)169 string RingtoneFileUtils::GetBaseNameFromPath(const string &path)
170 {
171 size_t found = path.rfind("/");
172 size_t foundDot = path.rfind(".");
173
174 string baseName = {};
175 found = (found == string::npos ? 0 : found);
176 if ((foundDot > found) && (foundDot != string::npos)) {
177 baseName = path.substr(found + 1, foundDot - found - 1);
178 } else {
179 baseName = "";
180 }
181
182 return baseName;
183 }
184
IsSameFile(const string & srcPath,const string & dstPath)185 bool RingtoneFileUtils::IsSameFile(const string &srcPath, const string &dstPath)
186 {
187 struct stat srcStatInfo {};
188 struct stat dstStatInfo {};
189
190 if (access(srcPath.c_str(), F_OK) || access(dstPath.c_str(), F_OK)) {
191 return false;
192 }
193 if (stat(srcPath.c_str(), &srcStatInfo) != 0) {
194 RINGTONE_ERR_LOG("Failed to get file %{private}s StatInfo, err=%{public}d", srcPath.c_str(), errno);
195 return false;
196 }
197 if (stat(dstPath.c_str(), &dstStatInfo) != 0) {
198 RINGTONE_ERR_LOG("Failed to get file %{private}s StatInfo, err=%{public}d", dstPath.c_str(), errno);
199 return false;
200 }
201 if (srcStatInfo.st_size != dstStatInfo.st_size) { /* file size */
202 RINGTONE_INFO_LOG("Size differs, srcStatInfo.st_size != dstStatInfo.st_size");
203 return false;
204 }
205
206 return true;
207 }
208
UnlinkCb(const char * fpath,const struct stat * sb,int32_t typeflag,struct FTW * ftwbuf)209 static int32_t UnlinkCb(const char *fpath, const struct stat *sb, int32_t typeflag, struct FTW *ftwbuf)
210 {
211 CHECK_AND_RETURN_RET_LOG(fpath != nullptr, E_FAIL, "fpath == nullptr");
212 int32_t errRet = remove(fpath);
213 if (errRet) {
214 RINGTONE_ERR_LOG("Failed to remove errno: %{public}d, path: %{private}s", errno, fpath);
215 }
216
217 return errRet;
218 }
219
RemoveDirectory(const string & path)220 int32_t RingtoneFileUtils::RemoveDirectory(const string &path)
221 {
222 return nftw(path.c_str(), UnlinkCb, OPEN_FDS, FTW_DEPTH | FTW_PHYS);
223 }
224
Mkdir(const string & subStr,shared_ptr<int> errCodePtr)225 bool RingtoneFileUtils::Mkdir(const string &subStr, shared_ptr<int> errCodePtr)
226 {
227 mode_t mask = umask(0);
228 if (mkdir(subStr.c_str(), MODE_RWX_USR_GRP) == -1) {
229 if (errCodePtr != nullptr) {
230 *errCodePtr = errno;
231 }
232 RINGTONE_ERR_LOG("Failed to create directory %{public}d", errno);
233 umask(mask);
234 return (errno == EEXIST) ? true : false;
235 }
236 umask(mask);
237 return true;
238 }
239
IsDirectory(const string & dirName,shared_ptr<int> errCodePtr)240 bool RingtoneFileUtils::IsDirectory(const string &dirName, shared_ptr<int> errCodePtr)
241 {
242 struct stat statInfo {};
243
244 if (stat(dirName.c_str(), &statInfo) == 0) {
245 if (statInfo.st_mode & S_IFDIR) {
246 return true;
247 }
248 } else if (errCodePtr != nullptr) {
249 *errCodePtr = errno;
250 return false;
251 }
252
253 return false;
254 }
255
CreateDirectory(const string & dirPath,shared_ptr<int> errCodePtr)256 bool RingtoneFileUtils::CreateDirectory(const string &dirPath, shared_ptr<int> errCodePtr)
257 {
258 string subStr;
259 string segment;
260
261 stringstream folderStream(dirPath);
262 while (getline(folderStream, segment, '/')) {
263 if (segment.empty()) {
264 continue;
265 }
266
267 subStr.append(RINGTONE_SLASH_CHAR + segment);
268 if (!IsDirectory(subStr, errCodePtr)) {
269 if (!Mkdir(subStr, errCodePtr)) {
270 return false;
271 }
272 }
273 }
274 return true;
275 }
276
OpenFile(const string & filePath,const string & mode)277 int32_t RingtoneFileUtils::OpenFile(const string &filePath, const string &mode)
278 {
279 int32_t errCode = E_ERR;
280
281 if (filePath.empty() || mode.empty()) {
282 RINGTONE_ERR_LOG("Invalid open argument! mode: %{private}s, path: %{private}s", mode.c_str(), filePath.c_str());
283 return errCode;
284 }
285
286 if (!IsTargetExtension(filePath)) {
287 return E_INVALID_PATH;
288 }
289
290 static const unordered_map<string, int32_t> RINGTONE_OPEN_MODE_MAP = {
291 { RINGTONE_FILEMODE_READONLY, O_RDONLY },
292 { RINGTONE_FILEMODE_WRITEONLY, O_WRONLY },
293 { RINGTONE_FILEMODE_READWRITE, O_RDWR },
294 { RINGTONE_FILEMODE_WRITETRUNCATE, O_WRONLY | O_TRUNC },
295 { RINGTONE_FILEMODE_WRITEAPPEND, O_WRONLY | O_APPEND },
296 { RINGTONE_FILEMODE_READWRITETRUNCATE, O_RDWR | O_TRUNC },
297 { RINGTONE_FILEMODE_READWRITEAPPEND, O_RDWR | O_APPEND },
298 };
299 if (RINGTONE_OPEN_MODE_MAP.find(mode) == RINGTONE_OPEN_MODE_MAP.end()) {
300 return E_ERR;
301 }
302
303 if (filePath.size() >= PATH_MAX) {
304 RINGTONE_ERR_LOG("File path too long %{public}d", (int)filePath.size());
305 return errCode;
306 }
307 string absFilePath;
308 RingtoneXCollie ringtoneXCollie("PathToRealPath time out",
309 [](void *) {
310 RINGTONE_INFO_LOG("PathToRealPath time out");
311 });
312 if (!PathToRealPath(filePath, absFilePath)) {
313 RINGTONE_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
314 ringtoneXCollie.CancelXCollieTimer();
315 return errCode;
316 }
317 ringtoneXCollie.CancelXCollieTimer();
318 if (absFilePath.empty()) {
319 RINGTONE_ERR_LOG("Failed to obtain the canonical path for source path %{public}d %{private}s",
320 errno, filePath.c_str());
321 return errCode;
322 }
323 RINGTONE_INFO_LOG("File absFilePath is %{private}s", absFilePath.c_str());
324 return open(absFilePath.c_str(), RINGTONE_OPEN_MODE_MAP.at(mode));
325 }
326
OpenVibrateFile(const std::string & filePath,const std::string & mode)327 int32_t RingtoneFileUtils::OpenVibrateFile(const std::string &filePath, const std::string &mode)
328 {
329 int32_t errCode = E_ERR;
330
331 if (filePath.empty() || mode.empty()) {
332 RINGTONE_ERR_LOG("Invalid open argument! mode: %{private}s, path: %{private}s", mode.c_str(), filePath.c_str());
333 return errCode;
334 }
335
336 if (!IsVibrateFile(filePath)) {
337 return E_INVALID_PATH;
338 }
339
340 static const unordered_map<string, int32_t> RINGTONE_OPEN_MODE_MAP = {
341 { RINGTONE_FILEMODE_READONLY, O_RDONLY },
342 { RINGTONE_FILEMODE_WRITEONLY, O_WRONLY },
343 { RINGTONE_FILEMODE_READWRITE, O_RDWR },
344 { RINGTONE_FILEMODE_WRITETRUNCATE, O_WRONLY | O_TRUNC },
345 { RINGTONE_FILEMODE_WRITEAPPEND, O_WRONLY | O_APPEND },
346 { RINGTONE_FILEMODE_READWRITETRUNCATE, O_RDWR | O_TRUNC },
347 { RINGTONE_FILEMODE_READWRITEAPPEND, O_RDWR | O_APPEND },
348 };
349 if (RINGTONE_OPEN_MODE_MAP.find(mode) == RINGTONE_OPEN_MODE_MAP.end()) {
350 return E_ERR;
351 }
352
353 if (filePath.size() >= PATH_MAX) {
354 RINGTONE_ERR_LOG("File path too long %{public}d", (int)filePath.size());
355 return errCode;
356 }
357 string absFilePath;
358 RingtoneXCollie ringtoneXCollie("VibrateFile PathToRealPath time out",
359 [](void *) {
360 RINGTONE_INFO_LOG("VibrateFile PathToRealPath time out");
361 });
362 if (!PathToRealPath(filePath, absFilePath)) {
363 RINGTONE_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
364 ringtoneXCollie.CancelXCollieTimer();
365 return errCode;
366 }
367 ringtoneXCollie.CancelXCollieTimer();
368 if (absFilePath.empty()) {
369 RINGTONE_ERR_LOG("Failed to obtain the canonical path for source path %{public}d %{private}s",
370 errno, filePath.c_str());
371 return errCode;
372 }
373 RINGTONE_INFO_LOG("File absFilePath is %{private}s", absFilePath.c_str());
374 return open(absFilePath.c_str(), RINGTONE_OPEN_MODE_MAP.at(mode));
375 }
376
IsFileExists(const string & fileName)377 bool RingtoneFileUtils::IsFileExists(const string &fileName)
378 {
379 struct stat statInfo {};
380
381 return ((stat(fileName.c_str(), &statInfo)) == 0);
382 }
383
CreateFile(const string & filePath)384 int32_t RingtoneFileUtils::CreateFile(const string &filePath)
385 {
386 int32_t errCode = E_ERR;
387
388 if (filePath.empty()) {
389 RINGTONE_ERR_LOG("Filepath is empty");
390 return E_VIOLATION_PARAMETERS;
391 }
392
393 if (!IsTargetExtension(filePath)) {
394 return E_INVALID_PATH;
395 }
396
397 if (IsFileExists(filePath)) {
398 RINGTONE_ERR_LOG("the file exists path: %{private}s", filePath.c_str());
399 return E_FILE_EXIST;
400 }
401
402 ofstream file(filePath);
403 if (!file) {
404 RINGTONE_ERR_LOG("Output file path could not be created errno %{public}d", errno);
405 return errCode;
406 }
407
408 file.close();
409
410 return E_SUCCESS;
411 }
412
DeleteFile(const string & fileName)413 bool RingtoneFileUtils::DeleteFile(const string &fileName)
414 {
415 return (remove(fileName.c_str()) == 0);
416 }
417
MoveFile(const string & oldPath,const string & newPath)418 bool RingtoneFileUtils::MoveFile(const string &oldPath, const string &newPath)
419 {
420 bool errRet = false;
421
422 if (IsFileExists(oldPath) && !IsFileExists(newPath)) {
423 errRet = (rename(oldPath.c_str(), newPath.c_str()) == 0);
424 }
425
426 return errRet;
427 }
428
CopyFileUtil(const string & filePath,const string & newPath)429 bool RingtoneFileUtils::CopyFileUtil(const string &filePath, const string &newPath)
430 {
431 struct stat fst{};
432 bool ret = false;
433 if (filePath.size() >= PATH_MAX) {
434 RINGTONE_ERR_LOG("File path too long %{public}d", static_cast<int>(filePath.size()));
435 return ret;
436 }
437 RINGTONE_INFO_LOG("File path is %{private}s", filePath.c_str());
438 string absFilePath;
439 RingtoneXCollie ringtoneXCollie("CopyFileUtil PathToRealPath time out",
440 [](void *) {
441 RINGTONE_INFO_LOG("CopyFileUtil PathToRealPath time out");
442 });
443 if (!PathToRealPath(filePath, absFilePath)) {
444 RINGTONE_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
445 ringtoneXCollie.CancelXCollieTimer();
446 return ret;
447 }
448 ringtoneXCollie.CancelXCollieTimer();
449 if (absFilePath.empty()) {
450 RINGTONE_ERR_LOG("Failed to obtain the canonical path for source path%{private}s %{public}d",
451 filePath.c_str(), errno);
452 return ret;
453 }
454
455 int32_t source = open(absFilePath.c_str(), O_RDONLY);
456 if (source == -1) {
457 RINGTONE_ERR_LOG("Open failed for source file");
458 return ret;
459 }
460
461 int32_t dest = open(newPath.c_str(), O_WRONLY | O_CREAT, MODE_RW_USR);
462 if (dest == -1) {
463 RINGTONE_ERR_LOG("Open failed for destination file %{public}d", errno);
464 close(source);
465 return ret;
466 }
467
468 if (fstat(source, &fst) == 0) {
469 // Copy file content
470 if (sendfile(dest, source, nullptr, fst.st_size) != E_ERR) {
471 // Copy ownership and mode of source file
472 if (fchown(dest, fst.st_uid, fst.st_gid) == 0 &&
473 fchmod(dest, fst.st_mode) == 0) {
474 ret= true;
475 }
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(×);
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 void 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;
667 }
668 if (!CopyRingtoneFolder(oldPath, newPath)) {
669 RINGTONE_ERR_LOG("Copy ringtone folder failed");
670 return;
671 }
672 std::filesystem::remove_all(oldPath);
673 if (std::filesystem::exists(OLD_RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH)) {
674 RINGTONE_ERR_LOG("remove ringtone folder failed, errno is %{public}d", errno);
675 return;
676 }
677 RINGTONE_INFO_LOG("Ringtone folder move successfully");
678 return;
679 }
680
AccessRingtoneDir()681 void RingtoneFileUtils::AccessRingtoneDir()
682 {
683 if (access(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str(), F_OK) != 0) {
684 CreateRingtoneDir();
685 return;
686 }
687
688 // 检查contacts目录是否创建
689 if (access(RINGTONE_CUSTOMIZED_CONTACTS_PATH.c_str(), F_OK) != 0) {
690 if (CreatePreloadFolder(RINGTONE_CUSTOMIZED_CONTACTS_PATH) != E_SUCCESS) {
691 RINGTONE_ERR_LOG("create contacts dir failed!");
692 }
693 }
694
695 struct stat fileStat;
696 if (stat(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str(), &fileStat) != 0) {
697 RINGTONE_ERR_LOG("stat dir failed, errno is %{public}d", errno);
698 return;
699 }
700 // 检查组的写权限
701 if ((fileStat.st_mode & S_IWGRP) != 0) {
702 return;
703 }
704 if (IsEmptyFolder(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str())) {
705 RINGTONE_ERR_LOG("The directory is empty and lacks group write permission.");
706 if (DeleteFile(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str())) {
707 RINGTONE_ERR_LOG("DeleteFile denied, errCode: %{public}d", errno);
708 }
709 CreateRingtoneDir();
710 } else { //rename and move file
711 RINGTONE_ERR_LOG("The directory is not empty and lacks group write permission.");
712 if (MoveFile(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str(),
713 RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str())) {
714 if (CreatePreloadFolder(RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str()) != E_SUCCESS) {
715 RINGTONE_ERR_LOG("Create Ringtone dir failed, errno is %{public}d", errno);
716 //restore dir
717 MoveFile(RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str(),
718 RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str());
719 return;
720 }
721 if (MoveDirectory(RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str(),
722 RINGTONE_CUSTOMIZED_BASE_RINGTONE_PATH.c_str()) != E_SUCCESS) {
723 RINGTONE_ERR_LOG("Move dir failed, errno is %{public}d", errno);
724 CreateRingtoneDir();
725 return;
726 }
727 if (DeleteFile(RINGTONE_CUSTOMIZED_BASE_RINGTONETMP_PATH.c_str())) {
728 RINGTONE_ERR_LOG("DeleteFile denied, errCode: %{public}d", errno);
729 }
730 } else {
731 RINGTONE_ERR_LOG("Move Ringtone dir failed, errno is %{public}d", errno);
732 }
733 }
734 return;
735 }
736
GetFileExtension(const string & path)737 string RingtoneFileUtils::GetFileExtension(const string &path)
738 {
739 if (!path.empty()) {
740 size_t dotIndex = path.rfind(".");
741 if (dotIndex != string::npos) {
742 return path.substr(dotIndex + 1);
743 }
744 }
745
746 RINGTONE_ERR_LOG("Failed to obtain file extension because given pathname is empty");
747 return "";
748 }
749 } // namespace Media
750 } // namespace OHOS
751