X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Ffilesys.cpp;h=5ffb4506ea3c8bb50dc774b3e9db1e930f1748ae;hb=298bb3d8f7b2e089b7bc2c6ebeb77eae9bf56b88;hp=356d3018deff2843816f19a87e486479240efa85;hpb=2ed3067bbed9844b434b53fb9cacb870e6ca97ea;p=dragonfireclient.git diff --git a/src/filesys.cpp b/src/filesys.cpp index 356d3018d..5ffb4506e 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -18,12 +18,15 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "filesys.h" -#include "strfnd.h" +#include "util/string.h" #include -#include -#include -#include +#include +#include +#include +#include #include "log.h" +#include "config.h" +#include "porting.h" namespace fs { @@ -32,53 +35,28 @@ namespace fs #define _WIN32_WINNT 0x0501 #include -#include -#include -#include +#include -#define BUFSIZE MAX_PATH - -std::vector GetDirListing(std::string pathstring) +std::vector GetDirListing(const std::string &pathstring) { std::vector listing; WIN32_FIND_DATA FindFileData; HANDLE hFind = INVALID_HANDLE_VALUE; DWORD dwError; - LPTSTR DirSpec; - INT retval; - - DirSpec = (LPTSTR) malloc (BUFSIZE); - - if( DirSpec == NULL ) - { - errorstream<<"GetDirListing: Insufficient memory available"< (BUFSIZE - 2)) - { - errorstream<<"GetDirListing: Input directory is too large."< GetDirListing(std::string pathstring) DirListNode node; node.name = FindFileData.cFileName; node.dir = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - if(node.name != "." && node.name != "..") + if (node.name != "." && node.name != "..") listing.push_back(node); // List all the other files in the directory. - while (FindNextFile(hFind, &FindFileData) != 0) - { + while (FindNextFile(hFind, &FindFileData) != 0) { DirListNode node; node.name = FindFileData.cFileName; node.dir = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; @@ -101,29 +78,17 @@ std::vector GetDirListing(std::string pathstring) dwError = GetLastError(); FindClose(hFind); - if (dwError != ERROR_NO_MORE_FILES) - { - errorstream<<"GetDirListing: FindNextFile error. Error is " - < content = GetDirListing(path); - for(int i=0; i content = GetDirListing(path); + for (const DirListNode &n: content) { + std::string fullpath = path + DIR_DELIM + n.name; + if (!RecursiveDelete(fullpath)) { + errorstream << "RecursiveDelete: Failed to recurse to " + << fullpath << std::endl; return false; } } + infostream << "RecursiveDelete: Deleting directory " << path << std::endl; + if (!RemoveDirectory(path.c_str())) { + errorstream << "Failed to recursively delete directory " + << path << std::endl; + return false; + } return true; } -bool DeleteSingleFileOrEmptyDirectory(std::string path) +bool DeleteSingleFileOrEmptyDirectory(const std::string &path) { DWORD attr = GetFileAttributes(path.c_str()); bool is_directory = (attr != INVALID_FILE_ATTRIBUTES && @@ -214,7 +171,7 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path) std::string TempPath() { - DWORD bufsize = GetTempPath(0, ""); + DWORD bufsize = GetTempPath(0, NULL); if(bufsize == 0){ errorstream<<"GetTempPath failed, error = "< #include -std::vector GetDirListing(std::string pathstring) +std::vector GetDirListing(const std::string &pathstring) { std::vector listing; - DIR *dp; - struct dirent *dirp; - if((dp = opendir(pathstring.c_str())) == NULL) { + DIR *dp; + struct dirent *dirp; + if((dp = opendir(pathstring.c_str())) == NULL) { //infostream<<"Error("<d_name[0]!='.'){ - DirListNode node; - node.name = dirp->d_name; - if(node.name == "." || node.name == "..") - continue; + if(strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0) + continue; - int isdir = -1; // -1 means unknown + DirListNode node; + node.name = dirp->d_name; - /* - POSIX doesn't define d_type member of struct dirent and - certain filesystems on glibc/Linux will only return - DT_UNKNOWN for the d_type member. + int isdir = -1; // -1 means unknown - Also we don't know whether symlinks are directories or not. - */ + /* + POSIX doesn't define d_type member of struct dirent and + certain filesystems on glibc/Linux will only return + DT_UNKNOWN for the d_type member. + + Also we don't know whether symlinks are directories or not. + */ #ifdef _DIRENT_HAVE_D_TYPE - if(dirp->d_type != DT_UNKNOWN && dirp->d_type != DT_LNK) - isdir = (dirp->d_type == DT_DIR); + if(dirp->d_type != DT_UNKNOWN && dirp->d_type != DT_LNK) + isdir = (dirp->d_type == DT_DIR); #endif /* _DIRENT_HAVE_D_TYPE */ - /* - Was d_type DT_UNKNOWN, DT_LNK or nonexistent? - If so, try stat(). - */ - if(isdir == -1) - { - struct stat statbuf; - if (stat((pathstring + "/" + node.name).c_str(), &statbuf)) - continue; - isdir = ((statbuf.st_mode & S_IFDIR) == S_IFDIR); - } - node.dir = isdir; - listing.push_back(node); + /* + Was d_type DT_UNKNOWN, DT_LNK or nonexistent? + If so, try stat(). + */ + if(isdir == -1) { + struct stat statbuf{}; + if (stat((pathstring + "/" + node.name).c_str(), &statbuf)) + continue; + isdir = ((statbuf.st_mode & S_IFDIR) == S_IFDIR); } - } - closedir(dp); + node.dir = isdir; + listing.push_back(node); + } + closedir(dp); return listing; } -bool CreateDir(std::string path) +bool CreateDir(const std::string &path) { int r = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - if(r == 0) - { + if (r == 0) { return true; } - else - { - // If already exists, return true - if(errno == EEXIST) - return true; - return false; - } + + // If already exists, return true + if (errno == EEXIST) + return true; + return false; + } -bool PathExists(std::string path) +bool PathExists(const std::string &path) { - struct stat st; + struct stat st{}; return (stat(path.c_str(),&st) == 0); } -bool IsDir(std::string path) +bool IsPathAbsolute(const std::string &path) { - struct stat statbuf; + return path[0] == '/'; +} + +bool IsDir(const std::string &path) +{ + struct stat statbuf{}; if(stat(path.c_str(), &statbuf)) return false; // Actually error; but certainly not a directory return ((statbuf.st_mode & S_IFDIR) == S_IFDIR); @@ -326,36 +284,35 @@ bool IsDirDelimiter(char c) return c == '/'; } -bool RecursiveDelete(std::string path) +bool RecursiveDelete(const std::string &path) { /* Execute the 'rm' command directly, by fork() and execve() */ - + infostream<<"Removing \""<(argv)); + // Execv shouldn't return. Failed. _exit(1); } @@ -366,27 +323,26 @@ bool RecursiveDelete(std::string path) pid_t tpid; do{ tpid = wait(&child_status); - //if(tpid != child_pid) process_terminated(tpid); }while(tpid != child_pid); return (child_status == 0); } } -bool DeleteSingleFileOrEmptyDirectory(std::string path) +bool DeleteSingleFileOrEmptyDirectory(const std::string &path) { - if(IsDir(path)){ + if (IsDir(path)) { bool did = (rmdir(path.c_str()) == 0); - if(!did) - errorstream<<"rmdir errno: "< &dst) +void GetRecursiveDirs(std::vector &dirs, const std::string &dir) { - std::vector content = GetDirListing(path); - for(unsigned int i=0; i chars_to_ignore = { '_', '.' }; + if (dir.empty() || !IsDir(dir)) + return; + dirs.push_back(dir); + fs::GetRecursiveSubPaths(dir, dirs, false, chars_to_ignore); } -bool DeletePaths(const std::vector &paths) +std::vector GetRecursiveDirs(const std::string &dir) { - bool success = true; - // Go backwards to succesfully delete the output of GetRecursiveSubPaths - for(int i=paths.size()-1; i>=0; i--){ - const std::string &path = paths[i]; - bool did = DeleteSingleFileOrEmptyDirectory(path); - if(!did){ - errorstream<<"Failed to delete "< result; + GetRecursiveDirs(result, dir); + return result; +} + +void GetRecursiveSubPaths(const std::string &path, + std::vector &dst, + bool list_files, + const std::set &ignore) +{ + std::vector content = GetDirListing(path); + for (const auto &n : content) { + std::string fullpath = path + DIR_DELIM + n.name; + if (ignore.count(n.name[0])) + continue; + if (list_files || n.dir) + dst.push_back(fullpath); + if (n.dir) + GetRecursiveSubPaths(fullpath, dst, list_files, ignore); } - return success; } -bool RecursiveDeleteContent(std::string path) +bool RecursiveDeleteContent(const std::string &path) { infostream<<"Removing content of \""< list = GetDirListing(path); - for(unsigned int i=0; i tocreate; @@ -468,7 +434,7 @@ bool CreateAllDirs(std::string path) return true; } -bool CopyFileContents(std::string source, std::string target) +bool CopyFileContents(const std::string &source, const std::string &target) { FILE *sourcefile = fopen(source.c_str(), "rb"); if(sourcefile == NULL){ @@ -522,7 +488,7 @@ bool CopyFileContents(std::string source, std::string target) return retval; } -bool CopyDir(std::string source, std::string target) +bool CopyDir(const std::string &source, const std::string &target) { if(PathExists(source)){ if(!PathExists(target)){ @@ -531,10 +497,10 @@ bool CopyDir(std::string source, std::string target) bool retval = true; std::vector content = fs::GetDirListing(source); - for(unsigned int i=0; i < content.size(); i++){ - std::string sourcechild = source + DIR_DELIM + content[i].name; - std::string targetchild = target + DIR_DELIM + content[i].name; - if(content[i].dir){ + for (const auto &dln : content) { + std::string sourcechild = source + DIR_DELIM + dln.name; + std::string targetchild = target + DIR_DELIM + dln.name; + if(dln.dir){ if(!fs::CopyDir(sourcechild, targetchild)){ retval = false; } @@ -547,12 +513,11 @@ bool CopyDir(std::string source, std::string target) } return retval; } - else { - return false; - } + + return false; } -bool PathStartsWith(std::string path, std::string prefix) +bool PathStartsWith(const std::string &path, const std::string &prefix) { size_t pathsize = path.size(); size_t pathpos = 0; @@ -602,7 +567,7 @@ bool PathStartsWith(std::string path, std::string prefix) } } -std::string RemoveLastPathComponent(std::string path, +std::string RemoveLastPathComponent(const std::string &path, std::string *removed, int count) { if(removed) @@ -638,51 +603,149 @@ std::string RemoveRelativePathComponents(std::string path) { size_t pos = path.size(); size_t dotdot_count = 0; - while(pos != 0){ + while (pos != 0) { size_t component_with_delim_end = pos; // skip a dir delimiter - while(pos != 0 && IsDirDelimiter(path[pos-1])) + while (pos != 0 && IsDirDelimiter(path[pos-1])) pos--; // strip a path component size_t component_end = pos; - while(pos != 0 && !IsDirDelimiter(path[pos-1])) + while (pos != 0 && !IsDirDelimiter(path[pos-1])) pos--; size_t component_start = pos; std::string component = path.substr(component_start, component_end - component_start); bool remove_this_component = false; - if(component == "."){ + if (component == ".") { remove_this_component = true; - } - else if(component == ".."){ + } else if (component == "..") { remove_this_component = true; dotdot_count += 1; - } - else if(dotdot_count != 0){ + } else if (dotdot_count != 0) { remove_this_component = true; dotdot_count -= 1; } - if(remove_this_component){ - while(pos != 0 && IsDirDelimiter(path[pos-1])) + if (remove_this_component) { + while (pos != 0 && IsDirDelimiter(path[pos-1])) pos--; - path = path.substr(0, pos) + DIR_DELIM + - path.substr(component_with_delim_end, - std::string::npos); - pos++; + if (component_start == 0) { + // We need to remove the delemiter too + path = path.substr(component_with_delim_end, std::string::npos); + } else { + path = path.substr(0, pos) + DIR_DELIM + + path.substr(component_with_delim_end, std::string::npos); + } + if (pos > 0) + pos++; } } - if(dotdot_count > 0) + if (dotdot_count > 0) return ""; // remove trailing dir delimiters pos = path.size(); - while(pos != 0 && IsDirDelimiter(path[pos-1])) + while (pos != 0 && IsDirDelimiter(path[pos-1])) pos--; return path.substr(0, pos); } +std::string AbsolutePath(const std::string &path) +{ +#ifdef _WIN32 + char *abs_path = _fullpath(NULL, path.c_str(), MAX_PATH); +#else + char *abs_path = realpath(path.c_str(), NULL); +#endif + if (!abs_path) return ""; + std::string abs_path_str(abs_path); + free(abs_path); + return abs_path_str; +} + +const char *GetFilenameFromPath(const char *path) +{ + const char *filename = strrchr(path, DIR_DELIM_CHAR); + // Consistent with IsDirDelimiter this function handles '/' too + if (DIR_DELIM_CHAR != '/') { + const char *tmp = strrchr(path, '/'); + if (tmp && tmp > filename) + filename = tmp; + } + return filename ? filename + 1 : path; +} + +bool safeWriteToFile(const std::string &path, const std::string &content) +{ + std::string tmp_file = path + ".~mt"; + + // Write to a tmp file + std::ofstream os(tmp_file.c_str(), std::ios::binary); + if (!os.good()) + return false; + os << content; + os.flush(); + os.close(); + if (os.fail()) { + // Remove the temporary file because writing it failed and it's useless. + remove(tmp_file.c_str()); + return false; + } + + bool rename_success = false; + + // Move the finished temporary file over the real file +#ifdef _WIN32 + // When creating the file, it can cause Windows Search indexer, virus scanners and other apps + // to query the file. This can make the move file call below fail. + // We retry up to 5 times, with a 1ms sleep between, before we consider the whole operation failed + int number_attempts = 0; + while (number_attempts < 5) { + rename_success = MoveFileEx(tmp_file.c_str(), path.c_str(), + MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH); + if (rename_success) + break; + sleep_ms(1); + ++number_attempts; + } +#else + // On POSIX compliant systems rename() is specified to be able to swap the + // file in place of the destination file, making this a truly error-proof + // transaction. + rename_success = rename(tmp_file.c_str(), path.c_str()) == 0; +#endif + if (!rename_success) { + warningstream << "Failed to write to file: " << path.c_str() << std::endl; + // Remove the temporary file because moving it over the target file + // failed. + remove(tmp_file.c_str()); + return false; + } + + return true; +} + +bool ReadFile(const std::string &path, std::string &out) +{ + std::ifstream is(path, std::ios::binary | std::ios::ate); + if (!is.good()) { + return false; + } + + auto size = is.tellg(); + out.resize(size); + is.seekg(0); + is.read(&out[0], size); + + return true; +} + +bool Rename(const std::string &from, const std::string &to) +{ + return rename(from.c_str(), to.c_str()) == 0; +} + } // namespace fs