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