1 /*
2 * Copyright (C) 2021 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 "Scanner"
16
17 #include "scanner_utils.h"
18
19 #include <cerrno>
20 #include <fstream>
21
22 #include "directory_ex.h"
23 #include "media_column.h"
24 #include "media_log.h"
25 #include "medialibrary_type_const.h"
26 namespace OHOS {
27 namespace Media {
28 using namespace std;
29
30 std::vector<size_t> ScannerUtils::skipList_;
31
32 // Check if file exists or not
IsExists(const string & path)33 bool ScannerUtils::IsExists(const string &path)
34 {
35 struct stat statInfo {};
36
37 if (path.empty()) {
38 MEDIA_ERR_LOG("Given path name is empty");
39 return false;
40 }
41
42 return ((stat(path.c_str(), &statInfo)) == ERR_SUCCESS);
43 }
44
45 // Get the file name from file URI
GetFileNameFromUri(const string & path)46 string ScannerUtils::GetFileNameFromUri(const string &path)
47 {
48 if (!path.empty()) {
49 size_t lastSlashPosition = path.rfind("/");
50 if (lastSlashPosition != string::npos) {
51 if (path.size() > lastSlashPosition) {
52 return path.substr(lastSlashPosition + 1);
53 }
54 }
55 }
56
57 MEDIA_ERR_LOG("Failed to obtain file name because given pathname is empty");
58 return "";
59 }
60
61 // Get file extension from the given filepath uri
GetFileExtension(const string & path)62 string ScannerUtils::GetFileExtension(const string &path)
63 {
64 if (!path.empty()) {
65 size_t dotIndex = path.rfind(".");
66 if (dotIndex != string::npos) {
67 return path.substr(dotIndex + 1);
68 }
69 }
70
71 MEDIA_ERR_LOG("Failed to obtain file extension because given pathname is empty");
72 return "";
73 }
74
75 // Check if the given path is a directory path
IsDirectory(const string & path)76 bool ScannerUtils::IsDirectory(const string &path)
77 {
78 struct stat s;
79
80 if (!path.empty()) {
81 if (stat(path.c_str(), &s) == 0) {
82 if (s.st_mode & S_IFDIR) {
83 return true;
84 }
85 }
86 }
87
88 MEDIA_ERR_LOG("Either path is empty or it is not a directory");
89 return false;
90 }
91
IsRegularFile(const string & path)92 bool ScannerUtils::IsRegularFile(const string &path)
93 {
94 struct stat s;
95 if (!path.empty()) {
96 if (stat(path.c_str(), &s) == 0) {
97 if (s.st_mode & S_IFREG) {
98 return true;
99 }
100 }
101 }
102
103 return false;
104 }
105
106 // Check if the given file starts with '.' , i.e. if it is hidden
IsFileHidden(const string & path)107 bool ScannerUtils::IsFileHidden(const string &path)
108 {
109 if (!path.empty()) {
110 string fileName = GetFileNameFromUri(path);
111 if (!fileName.empty() && fileName.at(0) == '.') {
112 return true;
113 }
114 }
115
116 return false;
117 }
118
119 // Get the parent path
GetParentPath(const string & path)120 string ScannerUtils::GetParentPath(const string &path)
121 {
122 if (!path.empty()) {
123 size_t lastSlashPosition = path.rfind("/");
124 if (lastSlashPosition != string::npos && path.size() > lastSlashPosition) {
125 return path.substr(0, lastSlashPosition);
126 }
127 }
128
129 MEDIA_ERR_LOG("Failed to obtain the parent path");
130 return "";
131 }
132
GetRootMediaDir(string & dir)133 void ScannerUtils::GetRootMediaDir(string &dir)
134 {
135 dir = ROOT_MEDIA_DIR;
136 }
137
GetFileTitle(const string & displayName)138 string ScannerUtils::GetFileTitle(const string &displayName)
139 {
140 string::size_type pos = displayName.find_last_of('.');
141 return (pos == string::npos) ? displayName : displayName.substr(0, pos);
142 }
143
IsDirHidden(const string & path)144 bool ScannerUtils::IsDirHidden(const string &path)
145 {
146 bool dirHid = false;
147
148 if (!path.empty()) {
149 string dirName = ScannerUtils::GetFileNameFromUri(path);
150 if (!dirName.empty() && dirName.at(0) == '.') {
151 MEDIA_DEBUG_LOG("hidden Directory, name:%{private}s path:%{private}s", dirName.c_str(), path.c_str());
152 return true;
153 }
154
155 string curPath = path;
156 string excludePath = curPath.append("/.nomedia");
157 // Check is the folder consist of .nomedia file
158 if (ScannerUtils::IsExists(excludePath)) {
159 return true;
160 }
161
162 // Check is the dir is part of skiplist
163 if (CheckSkipScanList(path)) {
164 MEDIA_DEBUG_LOG("skip Directory, path:%{private}s", path.c_str());
165 return true;
166 }
167 }
168
169 return dirHid;
170 }
171
IsDirHiddenRecursive(const string & path)172 bool ScannerUtils::IsDirHiddenRecursive(const string &path)
173 {
174 bool dirHid = false;
175 string curPath = path;
176
177 do {
178 dirHid = IsDirHidden(curPath);
179 if (dirHid) {
180 break;
181 }
182
183 curPath = ScannerUtils::GetParentPath(curPath);
184 if (curPath.empty()) {
185 break;
186 }
187 } while (true);
188
189 return dirHid;
190 }
191
192 // Initialize the skip list
InitSkipList()193 void ScannerUtils::InitSkipList()
194 {
195 hash<string> hashStr;
196 size_t hashPath;
197 string path;
198
199 /*
200 * 1. file path: in disk or hard code? path?
201 * 2. call_once: no need to init again if it is really empty
202 * 3. add lock
203 */
204 ifstream skipFile(SKIPLIST_FILE_PATH.c_str());
205 if (skipFile.is_open()) {
206 while (getline(skipFile, path)) {
207 hashPath = hashStr(path);
208 skipList_.insert(skipList_.begin(), hashPath);
209 }
210 skipFile.close();
211 }
212
213 return;
214 }
215
216 // Check if path is part of Skip scan list
CheckSkipScanList(const string & path)217 bool ScannerUtils::CheckSkipScanList(const string &path)
218 {
219 hash<string> hashStr;
220 size_t hashPath;
221
222 vector<string> list = {
223 { ROOT_MEDIA_DIR + AUDIO_BUCKET },
224 { ROOT_MEDIA_DIR + PHOTO_BUCKET },
225 };
226 // only skip path as "/storage/cloud/files/Photo" when scanDir
227 // doesn't work in scanFile as path likes "/storage/cloud/files/Photo/*"
228 if (find(list.begin(), list.end(), path) != list.end()) {
229 return true;
230 }
231 if (skipList_.empty()) {
232 InitSkipList();
233 }
234
235 hashPath = hashStr(path);
236 if (find(skipList_.begin(), skipList_.end(), hashPath) != skipList_.end()) {
237 return true;
238 }
239 return false;
240 }
241 } // namespace Media
242 } // namespace OHOS
243