PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/mysql-workbench-gpl-5.2.40-src/library/base/file_utilities.cpp

#
C++ | 466 lines | 384 code | 42 blank | 40 comment | 76 complexity | 51dd3b7a400e56d8ff7d17272da572cf MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, LGPL-2.1, BSD-3-Clause-No-Nuclear-License-2014, BSD-3-Clause
  1. /*
  2. * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the
  16. * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  17. * Boston, MA 02111-1307, USA.
  18. */
  19. #include "base/string_utilities.h"
  20. #include "base/file_utilities.h"
  21. #include <stdexcept>
  22. #include <glib.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <glib/gstdio.h>
  27. #ifdef _WIN32
  28. #include <windows.h>
  29. #else
  30. #include <errno.h>
  31. #include <fcntl.h>
  32. #include <sys/file.h>
  33. #endif
  34. namespace base {
  35. std::string format_file_error(const std::string &text, int err)
  36. {
  37. #ifdef _WIN32
  38. return strfmt("%s: error code %i", text.c_str(), err);
  39. #else
  40. return strfmt("%s: %s", text.c_str(), strerror(err));
  41. #endif
  42. }
  43. file_error::file_error(const std::string &text, int err)
  44. : std::runtime_error(format_file_error(text, err)), sys_error_code(err)
  45. {
  46. }
  47. error_code file_error::code()
  48. {
  49. #ifdef _WIN32
  50. switch (sys_error_code)
  51. {
  52. case 0:
  53. return success;
  54. case ERROR_FILE_NOT_FOUND:
  55. case ERROR_PATH_NOT_FOUND:
  56. return file_not_found;
  57. case ERROR_ALREADY_EXISTS:
  58. return already_exists;
  59. case ERROR_ACCESS_DENIED:
  60. return access_denied;
  61. default:
  62. return other_error;
  63. }
  64. #else
  65. switch (sys_error_code)
  66. {
  67. case 0:
  68. return success;
  69. case ENOENT:
  70. return file_not_found;
  71. case EEXIST:
  72. return already_exists;
  73. case EACCES:
  74. return access_denied;
  75. default:
  76. return other_error;
  77. }
  78. #endif
  79. }
  80. //--------------------------------------------------------------------------------------------------
  81. std::list<std::string> scan_for_files_matching(const std::string &pattern, bool recursive)
  82. {
  83. std::list<std::string> matches;
  84. gchar *path = g_dirname(pattern.c_str());
  85. if (!g_file_test(path, G_FILE_TEST_EXISTS))
  86. {
  87. g_free(path);
  88. return matches;
  89. }
  90. std::string pure_pattern = pattern.substr(strlen(path) + 1);
  91. GPatternSpec *pat = g_pattern_spec_new(g_basename(pattern.c_str()));
  92. GDir *dir;
  93. {
  94. GError *err = NULL;
  95. dir = g_dir_open(path ? path : ".", 0, &err);
  96. if (!dir)
  97. {
  98. std::string msg = strfmt("can't open %s: %s", path ? path : ".", err->message);
  99. g_error_free(err);
  100. g_pattern_spec_free(pat);
  101. throw std::runtime_error(msg);
  102. }
  103. }
  104. const gchar *filename;
  105. while ((filename = g_dir_read_name(dir)))
  106. {
  107. std::string full_path = strfmt("%s%s%s", path, G_DIR_SEPARATOR_S, filename);
  108. if (g_pattern_match_string(pat, filename))
  109. matches.push_back(full_path);
  110. if (recursive && g_file_test(full_path.c_str(), G_FILE_TEST_IS_DIR))
  111. {
  112. std::string subpattern = strfmt("%s%s%s", full_path.c_str(), G_DIR_SEPARATOR_S, pure_pattern.c_str());
  113. std::list<std::string> submatches = scan_for_files_matching(subpattern, true);
  114. if (submatches.size() > 0)
  115. matches.insert(matches.end(), submatches.begin(), submatches.end());
  116. }
  117. }
  118. g_dir_close(dir);
  119. g_pattern_spec_free(pat);
  120. return matches;
  121. }
  122. //--------------------------------------------------------------------------------------------------
  123. #ifdef _WIN32
  124. LockFile::LockFile(const std::string &path) throw (std::invalid_argument, std::runtime_error, file_locked_error)
  125. : path(path), handle(0)
  126. {
  127. std::wstring wpath(string_to_wstring(path));
  128. if (path.empty()) throw std::invalid_argument("invalid path");
  129. handle = CreateFileW(wpath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL,
  130. OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  131. if (handle == INVALID_HANDLE_VALUE)
  132. {
  133. if (GetLastError() == ERROR_SHARING_VIOLATION)
  134. throw file_locked_error("File already locked");
  135. throw std::runtime_error(strfmt("Error creating lock file (%i)", GetLastError()));
  136. }
  137. char buffer[32];
  138. sprintf(buffer, "%i", GetCurrentProcessId());
  139. DWORD bytes_written;
  140. if (!WriteFile(handle, buffer, strlen(buffer), &bytes_written, NULL) || bytes_written != strlen(buffer))
  141. {
  142. CloseHandle(handle);
  143. DeleteFileW(wpath.c_str());
  144. throw std::runtime_error("Could not write to lock file");
  145. }
  146. }
  147. LockFile::~LockFile()
  148. {
  149. if (handle)
  150. CloseHandle(handle);
  151. DeleteFileW(string_to_wstring(path).c_str());
  152. }
  153. LockFile::Status LockFile::check(const std::string &path)
  154. {
  155. std::wstring wpath(string_to_wstring(path));
  156. // open the file and see if it's locked
  157. HANDLE h = CreateFileW(wpath.c_str(),
  158. GENERIC_WRITE,
  159. FILE_SHARE_WRITE|FILE_SHARE_READ,
  160. NULL,
  161. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  162. if (h == INVALID_HANDLE_VALUE)
  163. {
  164. switch (GetLastError())
  165. {
  166. case ERROR_SHARING_VIOLATION:
  167. // if file cannot be opened for writing, it is locked...
  168. // so open it for reading to check the owner process id written in it
  169. h = CreateFileW(wpath.c_str(),
  170. GENERIC_READ,
  171. FILE_SHARE_WRITE|FILE_SHARE_READ,
  172. NULL,
  173. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  174. if (h != INVALID_HANDLE_VALUE)
  175. {
  176. char buffer[32];
  177. DWORD bytes_read;
  178. if (ReadFile(h, buffer, sizeof(buffer), &bytes_read, NULL))
  179. {
  180. CloseHandle(h);
  181. buffer[bytes_read]= 0;
  182. if (atoi(buffer) == GetCurrentProcessId())
  183. return LockedSelf;
  184. return LockedOther;
  185. }
  186. CloseHandle(h);
  187. return LockedOther;
  188. }
  189. // if the file is locked for read, assume its locked by some unrelated process
  190. // since this class never locks it for read
  191. return LockedOther;
  192. //throw std::runtime_error(strfmt("Could not read process id from lock file (%i)", GetLastError());
  193. break;
  194. case ERROR_FILE_NOT_FOUND:
  195. return NotLocked;
  196. case ERROR_PATH_NOT_FOUND:
  197. throw std::invalid_argument("Invalid path");
  198. default:
  199. throw std::runtime_error(strfmt("Could not open lock file (%i)", GetLastError()));
  200. }
  201. }
  202. else
  203. {
  204. CloseHandle(h);
  205. // if the file could be opened with DENY_WRITE, it means no-one is locking it
  206. return NotLocked;
  207. }
  208. }
  209. #else
  210. LockFile::LockFile(const std::string &apath) throw (std::invalid_argument, std::runtime_error, file_locked_error)
  211. : path(apath)
  212. {
  213. if (path.empty()) throw std::invalid_argument("invalid path");
  214. fd = open(path.c_str(), O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
  215. if (fd < 0)
  216. {
  217. // this could mean lock exists, that it's a dangling file or that it's currently being locked by some other process/thread
  218. // we just go on and try to lock the file if the file already exists
  219. if (errno == ENOENT || errno == ENOTDIR)
  220. throw std::invalid_argument("invalid path");
  221. throw std::runtime_error(strfmt("%s creating lock file", g_strerror(errno)));
  222. }
  223. if (flock(fd, LOCK_EX|LOCK_NB) < 0)
  224. {
  225. close(fd);
  226. fd = -1;
  227. if (errno == EWOULDBLOCK)
  228. throw file_locked_error("file already locked");
  229. throw std::runtime_error(strfmt("%s while locking file", g_strerror(errno)));
  230. }
  231. ftruncate(fd, 0);
  232. char pid[32];
  233. snprintf(pid, sizeof(pid), "%i", getpid());
  234. if (write(fd, pid, strlen(pid)+1) < 0)
  235. {
  236. close(fd);
  237. throw std::runtime_error(strfmt("%s while locking file", g_strerror(errno)));
  238. }
  239. }
  240. LockFile::~LockFile()
  241. {
  242. if (fd >= 0)
  243. close(fd);
  244. unlink(path.c_str());
  245. }
  246. LockFile::Status LockFile::check(const std::string &path)
  247. {
  248. int fd = open(path.c_str(), O_RDONLY);
  249. if (fd < 0)
  250. return NotLocked;
  251. if (flock(fd, LOCK_EX|LOCK_NB) < 0)
  252. {
  253. char pid[32];
  254. // couldn't lock file, check if we own it ourselves
  255. int c = read(fd, pid, sizeof(pid)-1);
  256. close(fd);
  257. if (c < 0)
  258. return LockedOther;
  259. pid[c] = 0;
  260. if (atoi(pid) != getpid())
  261. return LockedOther;
  262. return LockedSelf;
  263. }
  264. else // nobody holds a lock on the file, so this is a leftover
  265. {
  266. flock(fd, LOCK_UN);
  267. close(fd);
  268. return NotLocked;
  269. }
  270. }
  271. #endif
  272. bool create_directory(const std::string &path, int mode)
  273. {
  274. #ifdef _WIN32
  275. if (!CreateDirectoryW(path_from_utf8(path).c_str(), NULL))
  276. {
  277. if (GetLastError() == ERROR_ALREADY_EXISTS)
  278. return false;
  279. throw file_error(strfmt("Could not create directory %s",
  280. path.c_str()), GetLastError());
  281. }
  282. #else
  283. if (g_mkdir(path_from_utf8(path).c_str(), mode) < 0)
  284. {
  285. if (errno == EEXIST)
  286. return false;
  287. throw file_error(strfmt("Could not create directory %s",
  288. path.c_str()), errno);
  289. }
  290. #endif
  291. return true;
  292. }
  293. void rename(const std::string &from, const std::string &to)
  294. {
  295. #ifdef _WIN32
  296. if (!MoveFile(path_from_utf8(from).c_str(), path_from_utf8(to).c_str()))
  297. throw file_error(strfmt("Could not rename file %s to %s", from.c_str(), to.c_str()), GetLastError());
  298. #else
  299. if (::g_rename(path_from_utf8(from).c_str(), path_from_utf8(to).c_str()) < 0)
  300. throw file_error(strfmt("Could not rename file %s to %s", from.c_str(), to.c_str()), errno);
  301. #endif
  302. }
  303. bool remove_recursive(const std::string &path)
  304. {
  305. GError *error= NULL;
  306. GDir* dir;
  307. const char *dir_entry;
  308. gchar *entry_path;
  309. dir= g_dir_open(path.c_str(), 0, &error);
  310. if (!dir && error)
  311. return false;
  312. while ((dir_entry= g_dir_read_name(dir)))
  313. {
  314. entry_path= g_build_filename(path.c_str(), dir_entry, NULL);
  315. if (g_file_test(entry_path, G_FILE_TEST_IS_DIR))
  316. (void) remove_recursive(entry_path);
  317. else
  318. (void) ::g_remove(entry_path);
  319. g_free(entry_path);
  320. }
  321. (void) g_rmdir(path.c_str());
  322. g_dir_close(dir);
  323. return true;
  324. }
  325. bool remove(const std::string &path)
  326. {
  327. #ifdef _WIN32
  328. if (is_directory(path))
  329. {
  330. if (!RemoveDirectoryW(path_from_utf8(path).c_str()))
  331. {
  332. if (GetLastError() == ERROR_FILE_NOT_FOUND
  333. || GetLastError() == ERROR_PATH_NOT_FOUND)
  334. return false;
  335. throw file_error(strfmt("Could not delete file %s", path.c_str()), GetLastError());
  336. }
  337. }
  338. else
  339. {
  340. if (!DeleteFileW(path_from_utf8(path).c_str()))
  341. {
  342. if (GetLastError() == ERROR_FILE_NOT_FOUND)
  343. return false;
  344. throw file_error(strfmt("Could not delete file %s", path.c_str()), GetLastError());
  345. }
  346. }
  347. #else
  348. if (::g_remove(path_from_utf8(path).c_str()) < 0)
  349. {
  350. if (errno == ENOENT)
  351. return false;
  352. throw file_error(strfmt("Could not delete file %s", path.c_str()), errno);
  353. }
  354. #endif
  355. return true;
  356. }
  357. bool file_exists(const std::string &path)
  358. {
  359. char *f = g_filename_from_utf8(path.c_str(), -1, NULL, NULL, NULL);
  360. if (g_file_test(f, G_FILE_TEST_EXISTS))
  361. {
  362. g_free(f);
  363. return true;
  364. }
  365. g_free(f);
  366. return false;
  367. }
  368. bool is_directory(const std::string &path)
  369. {
  370. char *f = g_filename_from_utf8(path.c_str(), -1, NULL, NULL, NULL);
  371. if (g_file_test(f, G_FILE_TEST_IS_DIR))
  372. {
  373. g_free(f);
  374. return true;
  375. }
  376. g_free(f);
  377. return false;
  378. }
  379. /*
  380. std::string make_path(const std::string &path, const std::string &filename)
  381. {
  382. if (path.empty())
  383. return filename;
  384. if (path[path.size()-1] == '/' || path[path.size()-1] == '\\')
  385. return path+filename;
  386. return path+G_DIR_SEPARATOR+filename;
  387. }*/
  388. std::string extension(const std::string &path)
  389. {
  390. std::string::size_type p = path.rfind('.');
  391. if (p != std::string::npos)
  392. {
  393. std::string ext(path.substr(p));
  394. if (ext.find('/') != std::string::npos || ext.find('\\') != std::string::npos)
  395. return "";
  396. return ext;
  397. }
  398. return "";
  399. }
  400. std::string dirname(const std::string &path)
  401. {
  402. char *dn = g_path_get_dirname(path.c_str());
  403. std::string tmp(dn);
  404. g_free(dn);
  405. return tmp;
  406. }
  407. std::string basename(const std::string &path)
  408. {
  409. char *dn = g_path_get_basename(path.c_str());
  410. std::string tmp(dn);
  411. g_free(dn);
  412. return tmp;
  413. }
  414. std::string strip_extension(const std::string &path)
  415. {
  416. std::string ext;
  417. if (!(ext = extension(path)).empty())
  418. {
  419. return path.substr(0, path.size() - ext.size());
  420. }
  421. return path;
  422. }
  423. };