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