• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021-2022 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 "media_file_utils.h"
18 
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <fstream>
22 #include <ftw.h>
23 #include <regex>
24 #include <sstream>
25 #include <sys/sendfile.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <unordered_map>
29 
30 #include "directory_ex.h"
31 #include "media_log.h"
32 #include "medialibrary_db_const.h"
33 #include "medialibrary_errno.h"
34 #include "medialibrary_type_const.h"
35 
36 using namespace std;
37 
38 namespace OHOS {
39 namespace Media {
40 static const mode_t CHOWN_RWX_USR_GRP = 02770;
41 static const mode_t CHOWN_RW_USR_GRP = 0660;
42 static const size_t DISPLAYNAME_MAX = 255;
43 const int32_t OPEN_FDS = 64;
44 constexpr size_t EMPTY_DIR_ENTRY_COUNT = 2;  // Empty dir has 2 entry: . and ..
45 
UnlinkCb(const char * fpath,const struct stat * sb,int32_t typeflag,struct FTW * ftwbuf)46 int32_t UnlinkCb(const char *fpath, const struct stat *sb, int32_t typeflag, struct FTW *ftwbuf)
47 {
48     CHECK_AND_RETURN_RET_LOG(fpath != nullptr, E_FAIL, "fpath == nullptr");
49     int32_t errRet = remove(fpath);
50     if (errRet) {
51         MEDIA_ERR_LOG("Failed to remove path: %{private}s, errno: %{public}d", fpath, errno);
52     }
53 
54     return errRet;
55 }
56 
RemoveDirectory(const string & path)57 int32_t MediaFileUtils::RemoveDirectory(const string &path)
58 {
59     int32_t errCode;
60     char *dirPath = const_cast<char*>(path.c_str());
61 
62     errCode = nftw(dirPath, UnlinkCb, OPEN_FDS, FTW_DEPTH | FTW_PHYS);
63     return errCode;
64 }
65 
CreateDirectory(const string & dirPath)66 bool MediaFileUtils::CreateDirectory(const string &dirPath)
67 {
68     string subStr;
69     string segment;
70 
71     /*  Create directory and its sub directories if does not exist
72      *  take each string after '/' create directory if does not exist.
73      *  Created directory will be the base path for the next sub directory.
74      */
75 
76     stringstream folderStream(dirPath.c_str());
77     while (std::getline(folderStream, segment, '/')) {
78         if (segment == "")    // skip the first "/" in case of "/storage/media/local/files"
79             continue;
80 
81         subStr = subStr + SLASH_CHAR + segment;
82         if (!IsDirectory(subStr)) {
83             string folderPath = subStr;
84             mode_t mask = umask(0);
85             if (mkdir(folderPath.c_str(), CHOWN_RWX_USR_GRP) == -1) {
86                 MEDIA_ERR_LOG("Failed to create directory %{public}d", errno);
87                 umask(mask);
88                 return false;
89             }
90             umask(mask);
91         }
92     }
93 
94     return true;
95 }
96 
IsFileExists(const string & fileName)97 bool MediaFileUtils::IsFileExists(const string &fileName)
98 {
99     struct stat statInfo {};
100 
101     return ((stat(fileName.c_str(), &statInfo)) == SUCCESS);
102 }
103 
IsDirEmpty(const string & path)104 bool MediaFileUtils::IsDirEmpty(const string &path)
105 {
106     DIR *dir = opendir(path.c_str());
107     if (dir == nullptr) {
108         MEDIA_ERR_LOG("Failed to open dir:%{private}s, errno: %{public}d. Just return dir NOT empty.",
109             path.c_str(), errno);
110         return false;
111     }
112     struct dirent *ent = nullptr;
113     size_t entCount = 0;
114     while ((ent = readdir(dir)) != nullptr) {
115         if (++entCount > EMPTY_DIR_ENTRY_COUNT) {
116             break;
117         }
118     }
119     if (closedir(dir) < 0) {
120         MEDIA_ERR_LOG("Fail to closedir: %{private}s, errno: %{public}d.", path.c_str(), errno);
121     }
122     return (entCount > EMPTY_DIR_ENTRY_COUNT) ? false : true;
123 }
124 
GetFilename(const string & filePath)125 string MediaFileUtils::GetFilename(const string &filePath)
126 {
127     string fileName = "";
128 
129     if (!(filePath.empty())) {
130         size_t lastSlash = filePath.rfind("/");
131         if (lastSlash != string::npos) {
132             if (filePath.size() > (lastSlash + 1)) {
133                 fileName = filePath.substr(lastSlash + 1, filePath.length() - lastSlash);
134             }
135         }
136     }
137 
138     return fileName;
139 }
140 
IsDirectory(const string & dirName)141 bool MediaFileUtils::IsDirectory(const string &dirName)
142 {
143     struct stat statInfo {};
144     if (stat(dirName.c_str(), &statInfo) == SUCCESS) {
145         if (statInfo.st_mode & S_IFDIR) {
146             return true;
147         }
148     }
149 
150     return false;
151 }
152 
CreateFile(const string & filePath)153 bool MediaFileUtils::CreateFile(const string &filePath)
154 {
155     bool errCode = false;
156 
157     if (filePath.empty() || IsFileExists(filePath)) {
158         return errCode;
159     }
160 
161     ofstream file(filePath);
162     if (!file) {
163         MEDIA_ERR_LOG("Output file path could not be created");
164         return errCode;
165     }
166 
167     if (chmod(filePath.c_str(), CHOWN_RW_USR_GRP) == SUCCESS) {
168         errCode = true;
169     }
170 
171     file.close();
172 
173     return errCode;
174 }
175 
DeleteFile(const string & fileName)176 bool MediaFileUtils::DeleteFile(const string &fileName)
177 {
178     return (remove(fileName.c_str()) == SUCCESS);
179 }
180 
DeleteDir(const std::string & dirName)181 bool MediaFileUtils::DeleteDir(const std::string &dirName)
182 {
183     bool errRet = false;
184 
185     if (IsDirectory(dirName)) {
186         errRet = (RemoveDirectory(dirName) == SUCCESS);
187     }
188 
189     return errRet;
190 }
191 
MoveFile(const string & oldPath,const string & newPath)192 bool MediaFileUtils::MoveFile(const string &oldPath, const string &newPath)
193 {
194     bool errRet = false;
195 
196     if (IsFileExists(oldPath) && !IsFileExists(newPath)) {
197         errRet = (rename(oldPath.c_str(), newPath.c_str()) == SUCCESS);
198     }
199 
200     return errRet;
201 }
202 
CopyFileUtil(const string & filePath,const string & newPath)203 bool CopyFileUtil(const string &filePath, const string &newPath)
204 {
205     struct stat fst;
206     bool errCode = false;
207     if (filePath.size() >= PATH_MAX) {
208         MEDIA_ERR_LOG("File path too long %{public}d", static_cast<int>(filePath.size()));
209         return errCode;
210     }
211     MEDIA_INFO_LOG("File path is %{private}s", filePath.c_str());
212     std::string absFilePath = "";
213     if (!PathToRealPath(filePath, absFilePath)) {
214         MEDIA_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
215         return errCode;
216     }
217     if (absFilePath.empty()) {
218         MEDIA_ERR_LOG("Failed to obtain the canonical path for source path%{private}s %{public}d",
219                       filePath.c_str(), errno);
220         return errCode;
221     }
222 
223     int32_t source = open(absFilePath.c_str(), O_RDONLY);
224     if (source == -1) {
225         MEDIA_ERR_LOG("Open failed for source file");
226         return errCode;
227     }
228 
229     int32_t dest = open(newPath.c_str(), O_WRONLY | O_CREAT, CHOWN_RWX_USR_GRP);
230     if (dest == -1) {
231         MEDIA_ERR_LOG("Open failed for destination file %{public}d", errno);
232         close(source);
233         return errCode;
234     }
235 
236     if (fstat(source, &fst) == SUCCESS) {
237         // Copy file content
238         if (sendfile(dest, source, 0, fst.st_size) != E_ERR) {
239             // Copy ownership and mode of source file
240             if (fchown(dest, fst.st_uid, fst.st_gid) == SUCCESS &&
241                 fchmod(dest, fst.st_mode) == SUCCESS) {
242                 errCode = true;
243             }
244         }
245     }
246 
247     close(source);
248     close(dest);
249 
250     return errCode;
251 }
252 
CopyFile(const string & filePath,const string & newPath)253 bool MediaFileUtils::CopyFile(const string &filePath, const string &newPath)
254 {
255     string newPathCorrected;
256     bool errCode = false;
257 
258     if (!(newPath.empty()) && !(filePath.empty())) {
259         newPathCorrected = newPath + "/" + GetFilename(filePath);
260     } else {
261         MEDIA_ERR_LOG("Src filepath or dest filePath value cannot be empty");
262         return false;
263     }
264 
265     if (IsFileExists(filePath) && !IsFileExists(newPathCorrected)) {
266         errCode = true; // set to create file if directory exists
267         if (!(IsDirectory(newPath))) {
268             errCode = CreateDirectory(newPath);
269         }
270         if (errCode) {
271             string canonicalDirPath = "";
272             if (!PathToRealPath(newPath, canonicalDirPath)) {
273                 MEDIA_ERR_LOG("Failed to obtain the canonical path for newpath %{private}s %{public}d",
274                               filePath.c_str(), errno);
275                 return false;
276             }
277             newPathCorrected = canonicalDirPath + "/" + GetFilename(filePath);
278             errCode = CopyFileUtil(filePath, newPathCorrected);
279         }
280     }
281 
282     return errCode;
283 }
284 
RenameDir(const string & oldPath,const string & newPath)285 bool MediaFileUtils::RenameDir(const string &oldPath, const string &newPath)
286 {
287     bool errRet = false;
288 
289     if (IsDirectory(oldPath)) {
290         errRet = (rename(oldPath.c_str(), newPath.c_str()) == SUCCESS);
291         if (!errRet) {
292             MEDIA_ERR_LOG("Failed RenameDir errno %{public}d", errno);
293         }
294     }
295 
296     return errRet;
297 }
298 
CheckDisplayName(const std::string & displayName)299 bool MediaFileUtils::CheckDisplayName(const std::string &displayName)
300 {
301     size_t size = displayName.length();
302     if (size == 0 || size > DISPLAYNAME_MAX) {
303         MEDIA_ERR_LOG("display name size err, size = %{public}zu", size);
304         return false;
305     }
306     std::regex express("[\\\\/:*?\"\'`<>|{}\\[\\]]");
307     bool bValid = std::regex_search(displayName, express);
308     if ((displayName.at(0) == '.') || bValid) {
309         MEDIA_ERR_LOG("CheckDisplayName fail %{private}s", displayName.c_str());
310         return false;
311     }
312     return true;
313 }
314 
CheckTitle(const std::string & title)315 bool MediaFileUtils::CheckTitle(const std::string &title)
316 {
317     size_t size = title.length();
318     if (size == 0 || size > DISPLAYNAME_MAX) {
319         MEDIA_ERR_LOG("title size err, size = %{public}zu", size);
320         return false;
321     }
322     std::regex express("[\\.\\\\/:*?\"\'`<>|{}\\[\\]]");
323     bool bValid = std::regex_search(title, express);
324     if (bValid) {
325         MEDIA_ERR_LOG("CheckTitle title fail %{private}s", title.c_str());
326     }
327     return !bValid;
328 }
329 
GetAlbumDateModified(const string & albumPath)330 int64_t MediaFileUtils::GetAlbumDateModified(const string &albumPath)
331 {
332     struct stat statInfo {};
333     if (!albumPath.empty() && stat(albumPath.c_str(), &statInfo) == 0) {
334         return (statInfo.st_mtime);
335     }
336     return 0;
337 }
338 
UTCTimeSeconds()339 int64_t MediaFileUtils::UTCTimeSeconds()
340 {
341     struct timespec t;
342     t.tv_sec = 0;
343     t.tv_nsec = 0;
344     clock_gettime(CLOCK_REALTIME, &t);
345     return (int64_t)(t.tv_sec);
346 }
GetNetworkIdFromUri(const string & uri)347 string MediaFileUtils::GetNetworkIdFromUri(const string &uri)
348 {
349     string networkId;
350     if (uri.empty()) {
351         return networkId;
352     }
353     size_t pos = uri.find(MEDIALIBRARY_DATA_ABILITY_PREFIX);
354     if (pos == string::npos) {
355         return networkId;
356     }
357     string tempUri = uri.substr(MEDIALIBRARY_DATA_ABILITY_PREFIX.length());
358     if (tempUri.empty()) {
359         return networkId;
360     }
361     pos = tempUri.find_first_of('/');
362     if (pos == 0 || pos == string::npos) {
363         return networkId;
364     }
365     networkId = tempUri.substr(0, pos);
366     return networkId;
367 }
368 
UpdatePath(const string & path,const string & uri)369 string MediaFileUtils::UpdatePath(const string &path, const string &uri)
370 {
371     string retStr = path;
372     MEDIA_INFO_LOG("MediaFileUtils::UpdatePath path = %{private}s, uri = %{private}s", path.c_str(), uri.c_str());
373     if (path.empty() || uri.empty()) {
374         return retStr;
375     }
376 
377     string networkId = GetNetworkIdFromUri(uri);
378     if (networkId.empty()) {
379         MEDIA_INFO_LOG("MediaFileUtils::UpdatePath retStr = %{private}s", retStr.c_str());
380         return retStr;
381     }
382 
383     size_t pos = path.find(MEDIA_DATA_DEVICE_PATH);
384     if (pos == string::npos) {
385         return retStr;
386     }
387 
388     string beginStr = path.substr(0, pos);
389     if (beginStr.empty()) {
390         return retStr;
391     }
392 
393     string endStr = path.substr(pos + MEDIA_DATA_DEVICE_PATH.length());
394     if (endStr.empty()) {
395         return retStr;
396     }
397 
398     retStr = beginStr + networkId + endStr;
399     MEDIA_INFO_LOG("MediaFileUtils::UpdatePath retStr = %{private}s", retStr.c_str());
400     return retStr;
401 }
402 
GetFileMediaTypeUri(int32_t mediaType,const string & networkId)403 string MediaFileUtils::GetFileMediaTypeUri(int32_t mediaType, const string &networkId)
404 {
405     string uri = MEDIALIBRARY_DATA_ABILITY_PREFIX + networkId + MEDIALIBRARY_DATA_URI_IDENTIFIER;
406     switch (mediaType) {
407         case MEDIA_TYPE_AUDIO:
408             return uri + MEDIALIBRARY_TYPE_AUDIO_URI;
409         case MEDIA_TYPE_VIDEO:
410             return uri + MEDIALIBRARY_TYPE_VIDEO_URI;
411         case MEDIA_TYPE_IMAGE:
412             return uri + MEDIALIBRARY_TYPE_IMAGE_URI;
413         case MEDIA_TYPE_FILE:
414         default:
415             return uri + MEDIALIBRARY_TYPE_FILE_URI;
416     }
417 }
418 
GetUriByNameAndId(const string & displayName,const string & networkId,int32_t id)419 string MediaFileUtils::GetUriByNameAndId(const string &displayName, const string &networkId, int32_t id)
420 {
421     MediaType mediaType = GetMediaType(displayName);
422     return MediaFileUtils::GetFileMediaTypeUri(mediaType, networkId) + SLASH_CHAR + to_string(id);
423 }
424 
GetMediaType(const std::string & filePath)425 MediaType MediaFileUtils::GetMediaType(const std::string &filePath)
426 {
427     MediaType mediaType = MEDIA_TYPE_FILE;
428 
429     if (filePath.size() == 0) {
430         return MEDIA_TYPE_ALL;
431     }
432 
433     size_t dotIndex = filePath.rfind('.');
434     if (dotIndex != string::npos) {
435         string extension = filePath.substr(dotIndex + 1, filePath.length() - dotIndex);
436         transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
437         if (SUPPORTED_AUDIO_FORMATS_SET.find(extension) != SUPPORTED_AUDIO_FORMATS_SET.end()) {
438             mediaType = MEDIA_TYPE_AUDIO;
439         } else if (SUPPORTED_VIDEO_FORMATS_SET.find(extension) != SUPPORTED_VIDEO_FORMATS_SET.end()) {
440             mediaType = MEDIA_TYPE_VIDEO;
441         } else if (SUPPORTED_IMAGE_FORMATS_SET.find(extension) != SUPPORTED_IMAGE_FORMATS_SET.end()) {
442             mediaType = MEDIA_TYPE_IMAGE;
443         } else {
444             mediaType = MEDIA_TYPE_FILE;
445         }
446     }
447 
448     return mediaType;
449 }
450 
SplitByChar(const string & str,const char split)451 string MediaFileUtils::SplitByChar(const string &str, const char split)
452 {
453     size_t splitIndex = str.find_last_of(split);
454     return (splitIndex == string::npos) ? ("") : (str.substr(splitIndex + 1));
455 }
456 
GetExtensionFromPath(const string & path)457 string MediaFileUtils::GetExtensionFromPath(const string &path)
458 {
459     string extension = SplitByChar(path, '.');
460     if (!extension.empty()) {
461         transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
462     }
463     return extension;
464 }
465 
OpenFile(const string & filePath,const string & mode)466 int32_t MediaFileUtils::OpenFile(const string &filePath, const string &mode)
467 {
468     int32_t errCode = E_ERR;
469 
470     if (filePath.empty() || mode.empty()) {
471         MEDIA_ERR_LOG("Invalid open argument! mode: %{public}s, path: %{private}s", mode.c_str(), filePath.c_str());
472         return errCode;
473     }
474 
475     static const unordered_map<string, int32_t> MEDIA_OPEN_MODE_MAP = {
476         { MEDIA_FILEMODE_READONLY, O_RDONLY },
477         { MEDIA_FILEMODE_WRITEONLY, O_WRONLY },
478         { MEDIA_FILEMODE_READWRITE, O_RDWR },
479         { MEDIA_FILEMODE_WRITETRUNCATE, O_WRONLY | O_TRUNC },
480         { MEDIA_FILEMODE_WRITEAPPEND, O_WRONLY | O_APPEND },
481         { MEDIA_FILEMODE_READWRITETRUNCATE, O_RDWR | O_TRUNC },
482         { MEDIA_FILEMODE_READWRITEAPPEND, O_RDWR | O_APPEND },
483     };
484     if (MEDIA_OPEN_MODE_MAP.find(mode) == MEDIA_OPEN_MODE_MAP.end()) {
485         return E_ERR;
486     }
487 
488     if (filePath.size() >= PATH_MAX) {
489         MEDIA_ERR_LOG("File path too long %{public}d", (int)filePath.size());
490         return errCode;
491     }
492     string absFilePath;
493     if (!PathToRealPath(filePath, absFilePath)) {
494         MEDIA_ERR_LOG("file is not real path, file path: %{private}s", filePath.c_str());
495         return errCode;
496     }
497     if (absFilePath.empty()) {
498         MEDIA_ERR_LOG("Failed to obtain the canonical path for source path %{public}d %{private}s",
499                       errno, filePath.c_str());
500         return errCode;
501     }
502     MEDIA_INFO_LOG("File absFilePath is %{private}s", absFilePath.c_str());
503     return open(absFilePath.c_str(), MEDIA_OPEN_MODE_MAP.at(mode));
504 }
505 } // namespace Media
506 } // namespace OHOS
507