1// This file is part of OpenCV project.
2// It is subject to the license terms in the LICENSE file found in the top-level directory
3// of this distribution and at http://opencv.org/license.html.
4
5#include "../precomp.hpp"
6
7#include <opencv2/core/utils/configuration.private.hpp>
8
9#include <opencv2/core/utils/logger.hpp>
10
11#include "opencv2/core/utils/filesystem.private.hpp"
12#include "opencv2/core/utils/filesystem.hpp"
13
14//#define DEBUG_FS_UTILS
15
16#ifdef DEBUG_FS_UTILS
17#include <iostream>
18#define DBG(...) __VA_ARGS__
19#else
20#define DBG(...)
21#endif
22
23
24#if OPENCV_HAVE_FILESYSTEM_SUPPORT
25
26#ifdef _WIN32
27#define WIN32_LEAN_AND_MEAN
28#undef NOMINMAX
29#define NOMINMAX
30#include <windows.h>
31#include <direct.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <errno.h>
35#include <io.h>
36#include <stdio.h>
37#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __GNU__ || defined __EMSCRIPTEN__ || defined __QNX__ || defined __CYGWIN__
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41#include <unistd.h>
42#include <errno.h>
43#endif
44
45#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT
46
47namespace cv { namespace utils { namespace fs {
48
49#ifdef _WIN32
50static const char native_separator = '\\';
51#else
52static const char native_separator = '/';
53#endif
54
55static inline
56bool isPathSeparator(char c)
57{
58 return c == '/' || c == '\\';
59}
60
61cv::String join(const cv::String& base, const cv::String& path)
62{
63 if (base.empty())
64 return path;
65 if (path.empty())
66 return base;
67
68 bool baseSep = isPathSeparator(c: base[base.size() - 1]);
69 bool pathSep = isPathSeparator(c: path[0]);
70 String result;
71 if (baseSep && pathSep)
72 {
73 result = base + path.substr(pos: 1);
74 }
75 else if (!baseSep && !pathSep)
76 {
77 result = base + native_separator + path;
78 }
79 else
80 {
81 result = base + path;
82 }
83 return result;
84}
85
86CV_EXPORTS cv::String getParent(const cv::String &path)
87{
88 std::string::size_type loc = path.find_last_of(s: "/\\");
89 if (loc == std::string::npos)
90 return std::string();
91 return std::string(path, 0, loc);
92}
93
94CV_EXPORTS std::wstring getParent(const std::wstring& path)
95{
96 std::wstring::size_type loc = path.find_last_of(s: L"/\\");
97 if (loc == std::wstring::npos)
98 return std::wstring();
99 return std::wstring(path, 0, loc);
100}
101
102#if OPENCV_HAVE_FILESYSTEM_SUPPORT
103
104cv::String canonical(const cv::String& path)
105{
106 cv::String result;
107#ifdef _WIN32
108 const char* result_str = _fullpath(NULL, path.c_str(), 0);
109#else
110 const char* result_str = realpath(name: path.c_str(), NULL);
111#endif
112 if (result_str)
113 {
114 result = cv::String(result_str);
115 free(ptr: (void*)result_str);
116 }
117 return result.empty() ? path : result;
118}
119
120
121bool exists(const cv::String& path)
122{
123 CV_INSTRUMENT_REGION();
124
125#if defined _WIN32 || defined WINCE
126 BOOL status = TRUE;
127 {
128 WIN32_FILE_ATTRIBUTE_DATA all_attrs;
129#ifdef WINRT
130 wchar_t wpath[MAX_PATH];
131 size_t copied = mbstowcs(wpath, path.c_str(), MAX_PATH);
132 CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
133 status = ::GetFileAttributesExW(wpath, GetFileExInfoStandard, &all_attrs);
134#else
135 status = ::GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &all_attrs);
136#endif
137 }
138
139 return !!status;
140#else
141 struct stat stat_buf;
142 return (0 == stat(file: path.c_str(), buf: &stat_buf));
143#endif
144}
145
146CV_EXPORTS void remove_all(const cv::String& path)
147{
148 if (!exists(path))
149 return;
150 if (isDirectory(path))
151 {
152 std::vector<String> entries;
153 utils::fs::glob(directory: path, pattern: cv::String(), result&: entries, recursive: false, includeDirectories: true);
154 for (size_t i = 0; i < entries.size(); i++)
155 {
156 const String& e = entries[i];
157 remove_all(path: e);
158 }
159#ifdef _MSC_VER
160 bool result = _rmdir(path.c_str()) == 0;
161#else
162 bool result = rmdir(path: path.c_str()) == 0;
163#endif
164 if (!result)
165 {
166 CV_LOG_ERROR(NULL, "Can't remove directory: " << path);
167 }
168 }
169 else
170 {
171#ifdef _MSC_VER
172 bool result = _unlink(path.c_str()) == 0;
173#else
174 bool result = unlink(name: path.c_str()) == 0;
175#endif
176 if (!result)
177 {
178 CV_LOG_ERROR(NULL, "Can't remove file: " << path);
179 }
180 }
181}
182
183
184cv::String getcwd()
185{
186 CV_INSTRUMENT_REGION();
187 cv::AutoBuffer<char, 4096> buf;
188#if defined WIN32 || defined _WIN32 || defined WINCE
189#ifdef WINRT
190 return cv::String();
191#else
192 DWORD sz = GetCurrentDirectoryA(0, NULL);
193 buf.allocate((size_t)sz);
194 sz = GetCurrentDirectoryA((DWORD)buf.size(), buf.data());
195 return cv::String(buf.data(), (size_t)sz);
196#endif
197#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __EMSCRIPTEN__ || defined __QNX__ || defined __CYGWIN__
198 for(;;)
199 {
200 char* p = ::getcwd(buf: buf.data(), size: buf.size());
201 if (p == NULL)
202 {
203 if (errno == ERANGE)
204 {
205 buf.allocate(size: buf.size() * 2);
206 continue;
207 }
208 return cv::String();
209 }
210 break;
211 }
212 return cv::String(buf.data(), (size_t)strlen(s: buf.data()));
213#else
214 return cv::String();
215#endif
216}
217
218
219bool createDirectory(const cv::String& path)
220{
221 CV_INSTRUMENT_REGION();
222#if defined WIN32 || defined _WIN32 || defined WINCE
223#ifdef WINRT
224 wchar_t wpath[MAX_PATH];
225 size_t copied = mbstowcs(wpath, path.c_str(), MAX_PATH);
226 CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
227 int result = CreateDirectoryA(wpath, NULL) ? 0 : -1;
228#else
229 int result = _mkdir(path.c_str());
230#endif
231#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __EMSCRIPTEN__ || defined __QNX__ || defined __CYGWIN__
232 int result = mkdir(path: path.c_str(), mode: 0777);
233#else
234 int result = -1;
235#endif
236
237 if (result == -1)
238 {
239 return isDirectory(path);
240 }
241 return true;
242}
243
244bool createDirectories(const cv::String& path_)
245{
246 cv::String path = path_;
247 for (;;)
248 {
249 char last_char = path.empty() ? 0 : path[path.length() - 1];
250 if (isPathSeparator(c: last_char))
251 {
252 path = path.substr(pos: 0, n: path.length() - 1);
253 continue;
254 }
255 break;
256 }
257
258 if (path.empty() || path == "./" || path == ".\\" || path == ".")
259 return true;
260 if (isDirectory(path))
261 return true;
262
263 size_t pos = path.rfind(c: '/');
264 if (pos == cv::String::npos)
265 pos = path.rfind(c: '\\');
266 if (pos != cv::String::npos)
267 {
268 cv::String parent_directory = path.substr(pos: 0, n: pos);
269 if (!parent_directory.empty())
270 {
271 if (!createDirectories(path_: parent_directory))
272 return false;
273 }
274 }
275
276 return createDirectory(path);
277}
278
279#ifdef _WIN32
280
281struct FileLock::Impl
282{
283 Impl(const char* fname)
284 {
285 // http://support.microsoft.com/kb/316609
286 int numRetries = 5;
287 do
288 {
289 handle = ::CreateFileA(fname, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
290 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
291 if (INVALID_HANDLE_VALUE == handle)
292 {
293 if (ERROR_SHARING_VIOLATION == GetLastError())
294 {
295 numRetries--;
296 Sleep(250);
297 continue;
298 }
299 else
300 {
301 CV_Error_(Error::StsAssert, ("Can't open lock file: %s", fname));
302 }
303 }
304 break;
305 } while (numRetries > 0);
306 }
307 ~Impl()
308 {
309 if (INVALID_HANDLE_VALUE != handle)
310 {
311 ::CloseHandle(handle);
312 }
313 }
314
315 bool lock()
316 {
317 OVERLAPPED overlapped;
318 std::memset(&overlapped, 0, sizeof(overlapped));
319 return !!::LockFileEx(handle, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &overlapped);
320 }
321 bool unlock()
322 {
323 OVERLAPPED overlapped;
324 std::memset(&overlapped, 0, sizeof(overlapped));
325 return !!::UnlockFileEx(handle, 0, MAXDWORD, MAXDWORD, &overlapped);
326 }
327
328 bool lock_shared()
329 {
330 OVERLAPPED overlapped;
331 std::memset(&overlapped, 0, sizeof(overlapped));
332 return !!::LockFileEx(handle, 0, 0, MAXDWORD, MAXDWORD, &overlapped);
333 }
334 bool unlock_shared()
335 {
336 return unlock();
337 }
338
339 HANDLE handle;
340
341private:
342 Impl(const Impl&); // disabled
343 Impl& operator=(const Impl&); // disabled
344};
345
346#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __GNU__ || defined __EMSCRIPTEN__ || defined __QNX__ || defined __CYGWIN__
347
348struct FileLock::Impl
349{
350 Impl(const char* fname)
351 {
352 handle = ::open(file: fname, O_RDWR);
353 CV_Assert(handle != -1);
354 }
355 ~Impl()
356 {
357 if (handle >= 0)
358 ::close(fd: handle);
359 }
360
361 bool lock()
362 {
363 struct ::flock l;
364 std::memset(s: &l, c: 0, n: sizeof(l));
365 l.l_type = F_WRLCK;
366 l.l_whence = SEEK_SET;
367 l.l_start = 0;
368 l.l_len = 0;
369 DBG(std::cout << "Lock..." << std::endl);
370 bool res = -1 != ::fcntl(fd: handle, F_SETLKW, &l);
371 return res;
372 }
373 bool unlock()
374 {
375 struct ::flock l;
376 std::memset(s: &l, c: 0, n: sizeof(l));
377 l.l_type = F_UNLCK;
378 l.l_whence = SEEK_SET;
379 l.l_start = 0;
380 l.l_len = 0;
381 DBG(std::cout << "Unlock..." << std::endl);
382 bool res = -1 != ::fcntl(fd: handle, F_SETLK, &l);
383 return res;
384 }
385
386 bool lock_shared()
387 {
388 struct ::flock l;
389 std::memset(s: &l, c: 0, n: sizeof(l));
390 l.l_type = F_RDLCK;
391 l.l_whence = SEEK_SET;
392 l.l_start = 0;
393 l.l_len = 0;
394 DBG(std::cout << "Lock read..." << std::endl);
395 bool res = -1 != ::fcntl(fd: handle, F_SETLKW, &l);
396 return res;
397 }
398 bool unlock_shared()
399 {
400 return unlock();
401 }
402
403 int handle;
404
405private:
406 Impl(const Impl&); // disabled
407 Impl& operator=(const Impl&); // disabled
408};
409
410#endif
411
412FileLock::FileLock(const char* fname)
413 : pImpl(new Impl(fname))
414{
415 // nothing
416}
417FileLock::~FileLock()
418{
419 delete pImpl;
420 pImpl = NULL;
421}
422
423void FileLock::lock() { CV_Assert(pImpl->lock()); }
424void FileLock::unlock() { CV_Assert(pImpl->unlock()); }
425void FileLock::lock_shared() { CV_Assert(pImpl->lock_shared()); }
426void FileLock::unlock_shared() { CV_Assert(pImpl->unlock_shared()); }
427
428
429
430cv::String getCacheDirectory(const char* sub_directory_name, const char* configuration_name)
431{
432 String cache_path;
433 if (configuration_name)
434 {
435 cache_path = utils::getConfigurationParameterString(name: configuration_name, defaultValue: "");
436 }
437 if (cache_path.empty())
438 {
439 cv::String default_cache_path;
440#ifdef _WIN32
441 char tmp_path_buf[MAX_PATH+1] = {0};
442 DWORD res = GetTempPath(MAX_PATH, tmp_path_buf);
443 if (res > 0 && res <= MAX_PATH)
444 {
445 default_cache_path = tmp_path_buf;
446 }
447#elif defined __ANDROID__
448 // no defaults
449#elif defined __APPLE__
450 const std::string tmpdir_env = utils::getConfigurationParameterString("TMPDIR");
451 if (!tmpdir_env.empty() && utils::fs::isDirectory(tmpdir_env))
452 {
453 default_cache_path = tmpdir_env;
454 }
455 else
456 {
457 default_cache_path = "/tmp/";
458 CV_LOG_WARNING(NULL, "Using world accessible cache directory. This may be not secure: " << default_cache_path);
459 }
460#elif defined __linux__ || defined __HAIKU__ || defined __FreeBSD__ || defined __GNU__
461 // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
462 if (default_cache_path.empty())
463 {
464 const std::string xdg_cache_env = utils::getConfigurationParameterString(name: "XDG_CACHE_HOME");
465 if (!xdg_cache_env.empty() && utils::fs::isDirectory(path: xdg_cache_env))
466 {
467 default_cache_path = xdg_cache_env;
468 }
469 }
470 if (default_cache_path.empty())
471 {
472 const std::string home_env = utils::getConfigurationParameterString(name: "HOME");
473 if (!home_env.empty() && utils::fs::isDirectory(path: home_env))
474 {
475 cv::String home_path = home_env;
476 cv::String home_cache_path = utils::fs::join(base: home_path, path: ".cache/");
477 if (utils::fs::isDirectory(path: home_cache_path))
478 {
479 default_cache_path = home_cache_path;
480 }
481 }
482 }
483 if (default_cache_path.empty())
484 {
485 const char* temp_path = "/var/tmp/";
486 if (utils::fs::isDirectory(path: temp_path))
487 {
488 default_cache_path = temp_path;
489 CV_LOG_WARNING(NULL, "Using world accessible cache directory. This may be not secure: " << default_cache_path);
490 }
491 }
492 if (default_cache_path.empty())
493 {
494 default_cache_path = "/tmp/";
495 CV_LOG_WARNING(NULL, "Using world accessible cache directory. This may be not secure: " << default_cache_path);
496 }
497#else
498 // no defaults
499#endif
500 CV_LOG_VERBOSE(NULL, 0, "default_cache_path = " << default_cache_path);
501 if (!default_cache_path.empty())
502 {
503 if (utils::fs::isDirectory(path: default_cache_path))
504 {
505 cv::String default_cache_path_base = utils::fs::join(base: default_cache_path, path: "opencv");
506 default_cache_path = utils::fs::join(base: default_cache_path_base, path: "4.x" CV_VERSION_STATUS);
507 if (utils::getConfigurationParameterBool(name: "OPENCV_CACHE_SHOW_CLEANUP_MESSAGE", defaultValue: true)
508 && !utils::fs::isDirectory(path: default_cache_path))
509 {
510 std::vector<cv::String> existedCacheDirs;
511 try
512 {
513 utils::fs::glob_relative(directory: default_cache_path_base, pattern: "*", result&: existedCacheDirs, recursive: false, includeDirectories: true);
514 }
515 catch (...)
516 {
517 // ignore
518 }
519 if (!existedCacheDirs.empty())
520 {
521 CV_LOG_WARNING(NULL, "Creating new OpenCV cache directory: " << default_cache_path);
522 CV_LOG_WARNING(NULL, "There are several neighbour directories, probably created by old OpenCV versions.");
523 CV_LOG_WARNING(NULL, "Feel free to cleanup these unused directories:");
524 for (size_t i = 0; i < existedCacheDirs.size(); i++)
525 {
526 CV_LOG_WARNING(NULL, " - " << existedCacheDirs[i]);
527 }
528 CV_LOG_WARNING(NULL, "Note: This message is showed only once.");
529 }
530 }
531 if (sub_directory_name && sub_directory_name[0] != '\0')
532 default_cache_path = utils::fs::join(base: default_cache_path, path: cv::String(sub_directory_name) + native_separator);
533 if (!utils::fs::createDirectories(path_: default_cache_path))
534 {
535 CV_LOG_DEBUG(NULL, "Can't create OpenCV cache sub-directory: " << default_cache_path);
536 }
537 else
538 {
539 cache_path = default_cache_path;
540 }
541 }
542 else
543 {
544 CV_LOG_INFO(NULL, "Can't find default cache directory (does it exist?): " << default_cache_path);
545 }
546 }
547 else
548 {
549 CV_LOG_DEBUG(NULL, "OpenCV has no support to discover default cache directory on the current platform");
550 }
551 }
552 else
553 {
554 if (cache_path == "disabled")
555 return cache_path;
556 if (!isDirectory(path: cache_path))
557 {
558 CV_LOG_WARNING(NULL, "Specified non-existed directory, creating OpenCV sub-directory for caching purposes: " << cache_path);
559 if (!createDirectories(path_: cache_path))
560 {
561 CV_LOG_ERROR(NULL, "Can't create OpenCV cache sub-directory: " << cache_path);
562 cache_path.clear();
563 }
564 }
565 }
566 CV_Assert(cache_path.empty() || utils::fs::isDirectory(cache_path));
567 if (!cache_path.empty())
568 {
569 if (!isPathSeparator(c: cache_path[cache_path.size() - 1]))
570 {
571 cache_path += native_separator;
572 }
573 }
574 return cache_path;
575}
576
577#else
578#define NOT_IMPLEMENTED CV_Error(Error::StsNotImplemented, "");
579cv::String canonical(const cv::String& /*path*/) { NOT_IMPLEMENTED }
580bool exists(const cv::String& /*path*/) { NOT_IMPLEMENTED }
581void remove_all(const cv::String& /*path*/) { NOT_IMPLEMENTED }
582cv::String getcwd() { NOT_IMPLEMENTED }
583bool createDirectory(const cv::String& /*path*/) { NOT_IMPLEMENTED }
584bool createDirectories(const cv::String& /*path*/) { NOT_IMPLEMENTED }
585cv::String getCacheDirectory(const char* /*sub_directory_name*/, const char* /*configuration_name = NULL*/) { NOT_IMPLEMENTED }
586#undef NOT_IMPLEMENTED
587#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT
588
589}}} // namespace
590
591
592#if OPENCV_HAVE_FILESYSTEM_SUPPORT
593#include "plugin_loader.impl.hpp"
594#endif
595

source code of opencv/modules/core/src/utils/filesystem.cpp