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