filesys.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "filesys.h"
  17. #include "util/string.h"
  18. #include <iostream>
  19. #include <cstdio>
  20. #include <cstring>
  21. #include <cerrno>
  22. #include <fstream>
  23. #include "log.h"
  24. #include "config.h"
  25. #include "porting.h"
  26. namespace fs
  27. {
  28. #ifdef _WIN32 // WINDOWS
  29. #define _WIN32_WINNT 0x0501
  30. #include <windows.h>
  31. #include <shlwapi.h>
  32. std::vector<DirListNode> GetDirListing(const std::string &pathstring)
  33. {
  34. std::vector<DirListNode> listing;
  35. WIN32_FIND_DATA FindFileData;
  36. HANDLE hFind = INVALID_HANDLE_VALUE;
  37. DWORD dwError;
  38. std::string dirSpec = pathstring + "\\*";
  39. // Find the first file in the directory.
  40. hFind = FindFirstFile(dirSpec.c_str(), &FindFileData);
  41. if (hFind == INVALID_HANDLE_VALUE) {
  42. dwError = GetLastError();
  43. if (dwError != ERROR_FILE_NOT_FOUND && dwError != ERROR_PATH_NOT_FOUND) {
  44. errorstream << "GetDirListing: FindFirstFile error."
  45. << " Error is " << dwError << std::endl;
  46. }
  47. } else {
  48. // NOTE:
  49. // Be very sure to not include '..' in the results, it will
  50. // result in an epic failure when deleting stuff.
  51. DirListNode node;
  52. node.name = FindFileData.cFileName;
  53. node.dir = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
  54. if (node.name != "." && node.name != "..")
  55. listing.push_back(node);
  56. // List all the other files in the directory.
  57. while (FindNextFile(hFind, &FindFileData) != 0) {
  58. DirListNode node;
  59. node.name = FindFileData.cFileName;
  60. node.dir = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
  61. if(node.name != "." && node.name != "..")
  62. listing.push_back(node);
  63. }
  64. dwError = GetLastError();
  65. FindClose(hFind);
  66. if (dwError != ERROR_NO_MORE_FILES) {
  67. errorstream << "GetDirListing: FindNextFile error."
  68. << " Error is " << dwError << std::endl;
  69. listing.clear();
  70. return listing;
  71. }
  72. }
  73. return listing;
  74. }
  75. bool CreateDir(const std::string &path)
  76. {
  77. bool r = CreateDirectory(path.c_str(), NULL);
  78. if(r == true)
  79. return true;
  80. if(GetLastError() == ERROR_ALREADY_EXISTS)
  81. return true;
  82. return false;
  83. }
  84. bool PathExists(const std::string &path)
  85. {
  86. return (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES);
  87. }
  88. bool IsPathAbsolute(const std::string &path)
  89. {
  90. return !PathIsRelative(path.c_str());
  91. }
  92. bool IsDir(const std::string &path)
  93. {
  94. DWORD attr = GetFileAttributes(path.c_str());
  95. return (attr != INVALID_FILE_ATTRIBUTES &&
  96. (attr & FILE_ATTRIBUTE_DIRECTORY));
  97. }
  98. bool IsDirDelimiter(char c)
  99. {
  100. return c == '/' || c == '\\';
  101. }
  102. bool RecursiveDelete(const std::string &path)
  103. {
  104. infostream<<"Recursively deleting \""<<path<<"\""<<std::endl;
  105. DWORD attr = GetFileAttributes(path.c_str());
  106. bool is_directory = (attr != INVALID_FILE_ATTRIBUTES &&
  107. (attr & FILE_ATTRIBUTE_DIRECTORY));
  108. if(!is_directory)
  109. {
  110. infostream<<"RecursiveDelete: Deleting file "<<path<<std::endl;
  111. //bool did = DeleteFile(path.c_str());
  112. bool did = true;
  113. if(!did){
  114. errorstream<<"RecursiveDelete: Failed to delete file "
  115. <<path<<std::endl;
  116. return false;
  117. }
  118. }
  119. else
  120. {
  121. infostream<<"RecursiveDelete: Deleting content of directory "
  122. <<path<<std::endl;
  123. std::vector<DirListNode> content = GetDirListing(path);
  124. for(size_t i=0; i<content.size(); i++){
  125. const DirListNode &n = content[i];
  126. std::string fullpath = path + DIR_DELIM + n.name;
  127. bool did = RecursiveDelete(fullpath);
  128. if(!did){
  129. errorstream<<"RecursiveDelete: Failed to recurse to "
  130. <<fullpath<<std::endl;
  131. return false;
  132. }
  133. }
  134. infostream<<"RecursiveDelete: Deleting directory "<<path<<std::endl;
  135. //bool did = RemoveDirectory(path.c_str();
  136. bool did = true;
  137. if(!did){
  138. errorstream<<"Failed to recursively delete directory "
  139. <<path<<std::endl;
  140. return false;
  141. }
  142. }
  143. return true;
  144. }
  145. bool DeleteSingleFileOrEmptyDirectory(const std::string &path)
  146. {
  147. DWORD attr = GetFileAttributes(path.c_str());
  148. bool is_directory = (attr != INVALID_FILE_ATTRIBUTES &&
  149. (attr & FILE_ATTRIBUTE_DIRECTORY));
  150. if(!is_directory)
  151. {
  152. bool did = DeleteFile(path.c_str());
  153. return did;
  154. }
  155. else
  156. {
  157. bool did = RemoveDirectory(path.c_str());
  158. return did;
  159. }
  160. }
  161. std::string TempPath()
  162. {
  163. DWORD bufsize = GetTempPath(0, NULL);
  164. if(bufsize == 0){
  165. errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
  166. return "";
  167. }
  168. std::vector<char> buf(bufsize);
  169. DWORD len = GetTempPath(bufsize, &buf[0]);
  170. if(len == 0 || len > bufsize){
  171. errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
  172. return "";
  173. }
  174. return std::string(buf.begin(), buf.begin() + len);
  175. }
  176. #else // POSIX
  177. #include <sys/types.h>
  178. #include <dirent.h>
  179. #include <sys/stat.h>
  180. #include <sys/wait.h>
  181. #include <unistd.h>
  182. std::vector<DirListNode> GetDirListing(const std::string &pathstring)
  183. {
  184. std::vector<DirListNode> listing;
  185. DIR *dp;
  186. struct dirent *dirp;
  187. if((dp = opendir(pathstring.c_str())) == NULL) {
  188. //infostream<<"Error("<<errno<<") opening "<<pathstring<<std::endl;
  189. return listing;
  190. }
  191. while ((dirp = readdir(dp)) != NULL) {
  192. // NOTE:
  193. // Be very sure to not include '..' in the results, it will
  194. // result in an epic failure when deleting stuff.
  195. if(strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
  196. continue;
  197. DirListNode node;
  198. node.name = dirp->d_name;
  199. int isdir = -1; // -1 means unknown
  200. /*
  201. POSIX doesn't define d_type member of struct dirent and
  202. certain filesystems on glibc/Linux will only return
  203. DT_UNKNOWN for the d_type member.
  204. Also we don't know whether symlinks are directories or not.
  205. */
  206. #ifdef _DIRENT_HAVE_D_TYPE
  207. if(dirp->d_type != DT_UNKNOWN && dirp->d_type != DT_LNK)
  208. isdir = (dirp->d_type == DT_DIR);
  209. #endif /* _DIRENT_HAVE_D_TYPE */
  210. /*
  211. Was d_type DT_UNKNOWN, DT_LNK or nonexistent?
  212. If so, try stat().
  213. */
  214. if(isdir == -1) {
  215. struct stat statbuf{};
  216. if (stat((pathstring + "/" + node.name).c_str(), &statbuf))
  217. continue;
  218. isdir = ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
  219. }
  220. node.dir = isdir;
  221. listing.push_back(node);
  222. }
  223. closedir(dp);
  224. return listing;
  225. }
  226. bool CreateDir(const std::string &path)
  227. {
  228. int r = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  229. if (r == 0) {
  230. return true;
  231. }
  232. // If already exists, return true
  233. if (errno == EEXIST)
  234. return true;
  235. return false;
  236. }
  237. bool PathExists(const std::string &path)
  238. {
  239. struct stat st{};
  240. return (stat(path.c_str(),&st) == 0);
  241. }
  242. bool IsPathAbsolute(const std::string &path)
  243. {
  244. return path[0] == '/';
  245. }
  246. bool IsDir(const std::string &path)
  247. {
  248. struct stat statbuf{};
  249. if(stat(path.c_str(), &statbuf))
  250. return false; // Actually error; but certainly not a directory
  251. return ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
  252. }
  253. bool IsDirDelimiter(char c)
  254. {
  255. return c == '/';
  256. }
  257. bool RecursiveDelete(const std::string &path)
  258. {
  259. /*
  260. Execute the 'rm' command directly, by fork() and execve()
  261. */
  262. infostream<<"Removing \""<<path<<"\""<<std::endl;
  263. //return false;
  264. pid_t child_pid = fork();
  265. if(child_pid == 0)
  266. {
  267. // Child
  268. char argv_data[3][10000];
  269. strcpy(argv_data[0], "/bin/rm");
  270. strcpy(argv_data[1], "-rf");
  271. strncpy(argv_data[2], path.c_str(), 10000);
  272. char *argv[4];
  273. argv[0] = argv_data[0];
  274. argv[1] = argv_data[1];
  275. argv[2] = argv_data[2];
  276. argv[3] = NULL;
  277. verbosestream<<"Executing '"<<argv[0]<<"' '"<<argv[1]<<"' '"
  278. <<argv[2]<<"'"<<std::endl;
  279. execv(argv[0], argv);
  280. // Execv shouldn't return. Failed.
  281. _exit(1);
  282. }
  283. else
  284. {
  285. // Parent
  286. int child_status;
  287. pid_t tpid;
  288. do{
  289. tpid = wait(&child_status);
  290. //if(tpid != child_pid) process_terminated(tpid);
  291. }while(tpid != child_pid);
  292. return (child_status == 0);
  293. }
  294. }
  295. bool DeleteSingleFileOrEmptyDirectory(const std::string &path)
  296. {
  297. if (IsDir(path)) {
  298. bool did = (rmdir(path.c_str()) == 0);
  299. if (!did)
  300. errorstream << "rmdir errno: " << errno << ": " << strerror(errno)
  301. << std::endl;
  302. return did;
  303. }
  304. bool did = (unlink(path.c_str()) == 0);
  305. if (!did)
  306. errorstream << "unlink errno: " << errno << ": " << strerror(errno)
  307. << std::endl;
  308. return did;
  309. }
  310. std::string TempPath()
  311. {
  312. /*
  313. Should the environment variables TMPDIR, TMP and TEMP
  314. and the macro P_tmpdir (if defined by stdio.h) be checked
  315. before falling back on /tmp?
  316. Probably not, because this function is intended to be
  317. compatible with lua's os.tmpname which under the default
  318. configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
  319. */
  320. #ifdef __ANDROID__
  321. return DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME DIR_DELIM "tmp";
  322. #else
  323. return DIR_DELIM "tmp";
  324. #endif
  325. }
  326. #endif
  327. void GetRecursiveDirs(std::vector<std::string> &dirs, const std::string &dir)
  328. {
  329. static const std::set<char> chars_to_ignore = { '_', '.' };
  330. if (dir.empty() || !IsDir(dir))
  331. return;
  332. dirs.push_back(dir);
  333. fs::GetRecursiveSubPaths(dir, dirs, false, chars_to_ignore);
  334. }
  335. std::vector<std::string> GetRecursiveDirs(const std::string &dir)
  336. {
  337. std::vector<std::string> result;
  338. GetRecursiveDirs(result, dir);
  339. return result;
  340. }
  341. void GetRecursiveSubPaths(const std::string &path,
  342. std::vector<std::string> &dst,
  343. bool list_files,
  344. const std::set<char> &ignore)
  345. {
  346. std::vector<DirListNode> content = GetDirListing(path);
  347. for (const auto &n : content) {
  348. std::string fullpath = path + DIR_DELIM + n.name;
  349. if (ignore.count(n.name[0]))
  350. continue;
  351. if (list_files || n.dir)
  352. dst.push_back(fullpath);
  353. if (n.dir)
  354. GetRecursiveSubPaths(fullpath, dst, list_files, ignore);
  355. }
  356. }
  357. bool DeletePaths(const std::vector<std::string> &paths)
  358. {
  359. bool success = true;
  360. // Go backwards to succesfully delete the output of GetRecursiveSubPaths
  361. for(int i=paths.size()-1; i>=0; i--){
  362. const std::string &path = paths[i];
  363. bool did = DeleteSingleFileOrEmptyDirectory(path);
  364. if(!did){
  365. errorstream<<"Failed to delete "<<path<<std::endl;
  366. success = false;
  367. }
  368. }
  369. return success;
  370. }
  371. bool RecursiveDeleteContent(const std::string &path)
  372. {
  373. infostream<<"Removing content of \""<<path<<"\""<<std::endl;
  374. std::vector<DirListNode> list = GetDirListing(path);
  375. for (const DirListNode &dln : list) {
  376. if(trim(dln.name) == "." || trim(dln.name) == "..")
  377. continue;
  378. std::string childpath = path + DIR_DELIM + dln.name;
  379. bool r = RecursiveDelete(childpath);
  380. if(!r) {
  381. errorstream << "Removing \"" << childpath << "\" failed" << std::endl;
  382. return false;
  383. }
  384. }
  385. return true;
  386. }
  387. bool CreateAllDirs(const std::string &path)
  388. {
  389. std::vector<std::string> tocreate;
  390. std::string basepath = path;
  391. while(!PathExists(basepath))
  392. {
  393. tocreate.push_back(basepath);
  394. basepath = RemoveLastPathComponent(basepath);
  395. if(basepath.empty())
  396. break;
  397. }
  398. for(int i=tocreate.size()-1;i>=0;i--)
  399. if(!CreateDir(tocreate[i]))
  400. return false;
  401. return true;
  402. }
  403. bool CopyFileContents(const std::string &source, const std::string &target)
  404. {
  405. FILE *sourcefile = fopen(source.c_str(), "rb");
  406. if(sourcefile == NULL){
  407. errorstream<<source<<": can't open for reading: "
  408. <<strerror(errno)<<std::endl;
  409. return false;
  410. }
  411. FILE *targetfile = fopen(target.c_str(), "wb");
  412. if(targetfile == NULL){
  413. errorstream<<target<<": can't open for writing: "
  414. <<strerror(errno)<<std::endl;
  415. fclose(sourcefile);
  416. return false;
  417. }
  418. size_t total = 0;
  419. bool retval = true;
  420. bool done = false;
  421. char readbuffer[BUFSIZ];
  422. while(!done){
  423. size_t readbytes = fread(readbuffer, 1,
  424. sizeof(readbuffer), sourcefile);
  425. total += readbytes;
  426. if(ferror(sourcefile)){
  427. errorstream<<source<<": IO error: "
  428. <<strerror(errno)<<std::endl;
  429. retval = false;
  430. done = true;
  431. }
  432. if(readbytes > 0){
  433. fwrite(readbuffer, 1, readbytes, targetfile);
  434. }
  435. if(feof(sourcefile) || ferror(sourcefile)){
  436. // flush destination file to catch write errors
  437. // (e.g. disk full)
  438. fflush(targetfile);
  439. done = true;
  440. }
  441. if(ferror(targetfile)){
  442. errorstream<<target<<": IO error: "
  443. <<strerror(errno)<<std::endl;
  444. retval = false;
  445. done = true;
  446. }
  447. }
  448. infostream<<"copied "<<total<<" bytes from "
  449. <<source<<" to "<<target<<std::endl;
  450. fclose(sourcefile);
  451. fclose(targetfile);
  452. return retval;
  453. }
  454. bool CopyDir(const std::string &source, const std::string &target)
  455. {
  456. if(PathExists(source)){
  457. if(!PathExists(target)){
  458. fs::CreateAllDirs(target);
  459. }
  460. bool retval = true;
  461. std::vector<DirListNode> content = fs::GetDirListing(source);
  462. for (const auto &dln : content) {
  463. std::string sourcechild = source + DIR_DELIM + dln.name;
  464. std::string targetchild = target + DIR_DELIM + dln.name;
  465. if(dln.dir){
  466. if(!fs::CopyDir(sourcechild, targetchild)){
  467. retval = false;
  468. }
  469. }
  470. else {
  471. if(!fs::CopyFileContents(sourcechild, targetchild)){
  472. retval = false;
  473. }
  474. }
  475. }
  476. return retval;
  477. }
  478. return false;
  479. }
  480. bool PathStartsWith(const std::string &path, const std::string &prefix)
  481. {
  482. size_t pathsize = path.size();
  483. size_t pathpos = 0;
  484. size_t prefixsize = prefix.size();
  485. size_t prefixpos = 0;
  486. for(;;){
  487. bool delim1 = pathpos == pathsize
  488. || IsDirDelimiter(path[pathpos]);
  489. bool delim2 = prefixpos == prefixsize
  490. || IsDirDelimiter(prefix[prefixpos]);
  491. if(delim1 != delim2)
  492. return false;
  493. if(delim1){
  494. while(pathpos < pathsize &&
  495. IsDirDelimiter(path[pathpos]))
  496. ++pathpos;
  497. while(prefixpos < prefixsize &&
  498. IsDirDelimiter(prefix[prefixpos]))
  499. ++prefixpos;
  500. if(prefixpos == prefixsize)
  501. return true;
  502. if(pathpos == pathsize)
  503. return false;
  504. }
  505. else{
  506. size_t len = 0;
  507. do{
  508. char pathchar = path[pathpos+len];
  509. char prefixchar = prefix[prefixpos+len];
  510. if(FILESYS_CASE_INSENSITIVE){
  511. pathchar = tolower(pathchar);
  512. prefixchar = tolower(prefixchar);
  513. }
  514. if(pathchar != prefixchar)
  515. return false;
  516. ++len;
  517. } while(pathpos+len < pathsize
  518. && !IsDirDelimiter(path[pathpos+len])
  519. && prefixpos+len < prefixsize
  520. && !IsDirDelimiter(
  521. prefix[prefixpos+len]));
  522. pathpos += len;
  523. prefixpos += len;
  524. }
  525. }
  526. }
  527. std::string RemoveLastPathComponent(const std::string &path,
  528. std::string *removed, int count)
  529. {
  530. if(removed)
  531. *removed = "";
  532. size_t remaining = path.size();
  533. for(int i = 0; i < count; ++i){
  534. // strip a dir delimiter
  535. while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
  536. remaining--;
  537. // strip a path component
  538. size_t component_end = remaining;
  539. while(remaining != 0 && !IsDirDelimiter(path[remaining-1]))
  540. remaining--;
  541. size_t component_start = remaining;
  542. // strip a dir delimiter
  543. while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
  544. remaining--;
  545. if(removed){
  546. std::string component = path.substr(component_start,
  547. component_end - component_start);
  548. if(i)
  549. *removed = component + DIR_DELIM + *removed;
  550. else
  551. *removed = component;
  552. }
  553. }
  554. return path.substr(0, remaining);
  555. }
  556. std::string RemoveRelativePathComponents(std::string path)
  557. {
  558. size_t pos = path.size();
  559. size_t dotdot_count = 0;
  560. while (pos != 0) {
  561. size_t component_with_delim_end = pos;
  562. // skip a dir delimiter
  563. while (pos != 0 && IsDirDelimiter(path[pos-1]))
  564. pos--;
  565. // strip a path component
  566. size_t component_end = pos;
  567. while (pos != 0 && !IsDirDelimiter(path[pos-1]))
  568. pos--;
  569. size_t component_start = pos;
  570. std::string component = path.substr(component_start,
  571. component_end - component_start);
  572. bool remove_this_component = false;
  573. if (component == ".") {
  574. remove_this_component = true;
  575. } else if (component == "..") {
  576. remove_this_component = true;
  577. dotdot_count += 1;
  578. } else if (dotdot_count != 0) {
  579. remove_this_component = true;
  580. dotdot_count -= 1;
  581. }
  582. if (remove_this_component) {
  583. while (pos != 0 && IsDirDelimiter(path[pos-1]))
  584. pos--;
  585. if (component_start == 0) {
  586. // We need to remove the delemiter too
  587. path = path.substr(component_with_delim_end, std::string::npos);
  588. } else {
  589. path = path.substr(0, pos) + DIR_DELIM +
  590. path.substr(component_with_delim_end, std::string::npos);
  591. }
  592. if (pos > 0)
  593. pos++;
  594. }
  595. }
  596. if (dotdot_count > 0)
  597. return "";
  598. // remove trailing dir delimiters
  599. pos = path.size();
  600. while (pos != 0 && IsDirDelimiter(path[pos-1]))
  601. pos--;
  602. return path.substr(0, pos);
  603. }
  604. std::string AbsolutePath(const std::string &path)
  605. {
  606. #ifdef _WIN32
  607. char *abs_path = _fullpath(NULL, path.c_str(), MAX_PATH);
  608. #else
  609. char *abs_path = realpath(path.c_str(), NULL);
  610. #endif
  611. if (!abs_path) return "";
  612. std::string abs_path_str(abs_path);
  613. free(abs_path);
  614. return abs_path_str;
  615. }
  616. const char *GetFilenameFromPath(const char *path)
  617. {
  618. const char *filename = strrchr(path, DIR_DELIM_CHAR);
  619. return filename ? filename + 1 : path;
  620. }
  621. bool safeWriteToFile(const std::string &path, const std::string &content)
  622. {
  623. std::string tmp_file = path + ".~mt";
  624. // Write to a tmp file
  625. std::ofstream os(tmp_file.c_str(), std::ios::binary);
  626. if (!os.good())
  627. return false;
  628. os << content;
  629. os.flush();
  630. os.close();
  631. if (os.fail()) {
  632. // Remove the temporary file because writing it failed and it's useless.
  633. remove(tmp_file.c_str());
  634. return false;
  635. }
  636. bool rename_success = false;
  637. // Move the finished temporary file over the real file
  638. #ifdef _WIN32
  639. // When creating the file, it can cause Windows Search indexer, virus scanners and other apps
  640. // to query the file. This can make the move file call below fail.
  641. // We retry up to 5 times, with a 1ms sleep between, before we consider the whole operation failed
  642. int number_attempts = 0;
  643. while (number_attempts < 5) {
  644. rename_success = MoveFileEx(tmp_file.c_str(), path.c_str(),
  645. MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
  646. if (rename_success)
  647. break;
  648. sleep_ms(1);
  649. ++number_attempts;
  650. }
  651. #else
  652. // On POSIX compliant systems rename() is specified to be able to swap the
  653. // file in place of the destination file, making this a truly error-proof
  654. // transaction.
  655. rename_success = rename(tmp_file.c_str(), path.c_str()) == 0;
  656. #endif
  657. if (!rename_success) {
  658. warningstream << "Failed to write to file: " << path.c_str() << std::endl;
  659. // Remove the temporary file because moving it over the target file
  660. // failed.
  661. remove(tmp_file.c_str());
  662. return false;
  663. }
  664. return true;
  665. }
  666. bool Rename(const std::string &from, const std::string &to)
  667. {
  668. return rename(from.c_str(), to.c_str()) == 0;
  669. }
  670. } // namespace fs