From 6a0ec6fcf6e45c9212535c481566e2454a7cc377 Mon Sep 17 00:00:00 2001 From: Reid Spencer Date: Wed, 29 Sep 2004 00:01:17 +0000 Subject: [PATCH] Improve validity checking of windows path names and fix file creation problems. Patch contributed by Jeff Cohen. Thanks Jeff! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@16565 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/System/Win32/Path.cpp | 109 +++++++++++++++++++++++++++++++++------------- lib/System/Win32/Path.inc | 109 +++++++++++++++++++++++++++++++++------------- 2 files changed, 156 insertions(+), 62 deletions(-) diff --git a/lib/System/Win32/Path.cpp b/lib/System/Win32/Path.cpp index b34987e4802..17d722beedf 100644 --- a/lib/System/Win32/Path.cpp +++ b/lib/System/Win32/Path.cpp @@ -24,6 +24,12 @@ #include #include +static void FlipBackSlashes(std::string& s) { + for (size_t i = 0; i < s.size(); i++) + if (s[i] == '\\') + s[i] = '/'; +} + namespace llvm { namespace sys { @@ -32,17 +38,35 @@ Path::is_valid() const { if (path.empty()) return false; - // On Unix, the realpath function is used, which only requires that the - // directories leading up the to final file name are valid. The file itself - // need not exist. To get this behavior on Windows, we must elide the file - // name manually. - Path dir(*this); - dir.elide_file(); - if (dir.path.empty()) - return true; + // If there is a colon, it must be the second character, preceded by a letter + // and followed by something. + size_t len = path.size(); + size_t pos = path.rfind(':',len); + if (pos != std::string::npos) { + if (pos != 1 || !isalpha(path[0]) || len < 3) + return false; + } - DWORD attr = GetFileAttributes(dir.path.c_str()); - return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY); + // Check for illegal characters. + if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012" + "\013\014\015\016\017\020\021\022\023\024\025\026" + "\027\030\031\032\033\034\035\036\037") + != std::string::npos) + return false; + + // A file or directory name may not end in a period. + if (path[len-1] == '.') + return false; + if (len >= 2 && path[len-2] == '.' && path[len-1] == '/') + return false; + + // A file or directory name may not end in a space. + if (path[len-1] == ' ') + return false; + if (len >= 2 && path[len-2] == ' ' && path[len-1] == '/') + return false; + + return true; } static Path *TempDirectory = NULL; @@ -54,7 +78,7 @@ Path::GetTemporaryDirectory() { char pathname[MAX_PATH]; if (!GetTempPath(MAX_PATH, pathname)) - ThrowError("Can't determine temporary directory"); + throw std::string("Can't determine temporary directory"); Path result; result.set_directory(pathname); @@ -77,13 +101,14 @@ Path::GetTemporaryDirectory() { Path::Path(std::string unverified_path) : path(unverified_path) { + FlipBackSlashes(path); if (unverified_path.empty()) return; if (this->is_valid()) return; // oops, not valid. path.clear(); - ThrowError(unverified_path + ": path is not valid"); + throw std::string(unverified_path + ": path is not valid"); } // FIXME: the following set of functions don't map to Windows very well. @@ -94,6 +119,11 @@ Path::GetRootDirectory() { return result; } +std::string +Path::GetDLLSuffix() { + return "dll"; +} + static inline bool IsLibrary(Path& path, const std::string& basename) { if (path.append_file(std::string("lib") + basename)) { if (path.append_suffix(Path::GetDLLSuffix()) && path.readable()) @@ -281,6 +311,7 @@ Path::set_directory(const std::string& a_path) { return false; Path save(*this); path = a_path; + FlipBackSlashes(path); size_t last = a_path.size() -1; if (last != 0 && a_path[last] != '/') path += '/'; @@ -297,6 +328,7 @@ Path::set_file(const std::string& a_path) { return false; Path save(*this); path = a_path; + FlipBackSlashes(path); size_t last = a_path.size() - 1; while (last > 0 && a_path[last] == '/') last--; @@ -396,31 +428,46 @@ Path::create_directory( bool create_parents) { // Get a writeable copy of the path name char *pathname = reinterpret_cast(_alloca(path.length()+1)); - path.copy(pathname,path.length()+1); - - // Null-terminate the last component - int lastchar = path.length() - 1 ; - if (pathname[lastchar] == '/') - pathname[lastchar] = 0; + path.copy(pathname,path.length()); + pathname[path.length()] = 0; + + // Determine starting point for initial / search. + char *next = pathname; + if (pathname[0] == '/' && pathname[1] == '/') { + // Skip host name. + next = strchr(pathname+2, '/'); + if (next == NULL) + throw std::string(pathname) + ": badly formed remote directory"; + // Skip share name. + next = strchr(next+1, '/'); + if (next == NULL) + throw std::string(pathname) + ": badly formed remote directory"; + next++; + if (*next == 0) + throw std::string(pathname) + ": badly formed remote directory"; + } else { + if (pathname[1] == ':') + next += 2; // skip drive letter + if (*next == '/') + next++; // skip root directory + } // If we're supposed to create intermediate directories - if ( create_parents ) { - // Find the end of the initial name component - char * next = strchr(pathname,'/'); - if ( pathname[0] == '/') - next = strchr(&pathname[1],'/'); - + if (create_parents) { // Loop through the directory components until we're done - while ( next != 0 ) { + while (*next) { + next = strchr(next, '/'); *next = 0; if (!CreateDirectory(pathname, NULL)) ThrowError(std::string(pathname) + ": Can't create directory: "); - char* save = next; - next = strchr(pathname,'/'); - *save = '/'; + *next++ = '/'; + } + } else { + // Drop trailing slash. + pathname[path.size()-1] = 0; + if (!CreateDirectory(pathname, NULL)) { + ThrowError(std::string(pathname) + ": Can't create directory: "); } - } else if (!CreateDirectory(pathname, NULL)) { - ThrowError(std::string(pathname) + ": Can't create directory: "); } return true; } @@ -431,7 +478,7 @@ Path::create_file() { if (!is_file()) return false; // Create the file - HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) ThrowError(std::string(path.c_str()) + ": Can't create file: "); diff --git a/lib/System/Win32/Path.inc b/lib/System/Win32/Path.inc index b34987e4802..17d722beedf 100644 --- a/lib/System/Win32/Path.inc +++ b/lib/System/Win32/Path.inc @@ -24,6 +24,12 @@ #include #include +static void FlipBackSlashes(std::string& s) { + for (size_t i = 0; i < s.size(); i++) + if (s[i] == '\\') + s[i] = '/'; +} + namespace llvm { namespace sys { @@ -32,17 +38,35 @@ Path::is_valid() const { if (path.empty()) return false; - // On Unix, the realpath function is used, which only requires that the - // directories leading up the to final file name are valid. The file itself - // need not exist. To get this behavior on Windows, we must elide the file - // name manually. - Path dir(*this); - dir.elide_file(); - if (dir.path.empty()) - return true; + // If there is a colon, it must be the second character, preceded by a letter + // and followed by something. + size_t len = path.size(); + size_t pos = path.rfind(':',len); + if (pos != std::string::npos) { + if (pos != 1 || !isalpha(path[0]) || len < 3) + return false; + } - DWORD attr = GetFileAttributes(dir.path.c_str()); - return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY); + // Check for illegal characters. + if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012" + "\013\014\015\016\017\020\021\022\023\024\025\026" + "\027\030\031\032\033\034\035\036\037") + != std::string::npos) + return false; + + // A file or directory name may not end in a period. + if (path[len-1] == '.') + return false; + if (len >= 2 && path[len-2] == '.' && path[len-1] == '/') + return false; + + // A file or directory name may not end in a space. + if (path[len-1] == ' ') + return false; + if (len >= 2 && path[len-2] == ' ' && path[len-1] == '/') + return false; + + return true; } static Path *TempDirectory = NULL; @@ -54,7 +78,7 @@ Path::GetTemporaryDirectory() { char pathname[MAX_PATH]; if (!GetTempPath(MAX_PATH, pathname)) - ThrowError("Can't determine temporary directory"); + throw std::string("Can't determine temporary directory"); Path result; result.set_directory(pathname); @@ -77,13 +101,14 @@ Path::GetTemporaryDirectory() { Path::Path(std::string unverified_path) : path(unverified_path) { + FlipBackSlashes(path); if (unverified_path.empty()) return; if (this->is_valid()) return; // oops, not valid. path.clear(); - ThrowError(unverified_path + ": path is not valid"); + throw std::string(unverified_path + ": path is not valid"); } // FIXME: the following set of functions don't map to Windows very well. @@ -94,6 +119,11 @@ Path::GetRootDirectory() { return result; } +std::string +Path::GetDLLSuffix() { + return "dll"; +} + static inline bool IsLibrary(Path& path, const std::string& basename) { if (path.append_file(std::string("lib") + basename)) { if (path.append_suffix(Path::GetDLLSuffix()) && path.readable()) @@ -281,6 +311,7 @@ Path::set_directory(const std::string& a_path) { return false; Path save(*this); path = a_path; + FlipBackSlashes(path); size_t last = a_path.size() -1; if (last != 0 && a_path[last] != '/') path += '/'; @@ -297,6 +328,7 @@ Path::set_file(const std::string& a_path) { return false; Path save(*this); path = a_path; + FlipBackSlashes(path); size_t last = a_path.size() - 1; while (last > 0 && a_path[last] == '/') last--; @@ -396,31 +428,46 @@ Path::create_directory( bool create_parents) { // Get a writeable copy of the path name char *pathname = reinterpret_cast(_alloca(path.length()+1)); - path.copy(pathname,path.length()+1); - - // Null-terminate the last component - int lastchar = path.length() - 1 ; - if (pathname[lastchar] == '/') - pathname[lastchar] = 0; + path.copy(pathname,path.length()); + pathname[path.length()] = 0; + + // Determine starting point for initial / search. + char *next = pathname; + if (pathname[0] == '/' && pathname[1] == '/') { + // Skip host name. + next = strchr(pathname+2, '/'); + if (next == NULL) + throw std::string(pathname) + ": badly formed remote directory"; + // Skip share name. + next = strchr(next+1, '/'); + if (next == NULL) + throw std::string(pathname) + ": badly formed remote directory"; + next++; + if (*next == 0) + throw std::string(pathname) + ": badly formed remote directory"; + } else { + if (pathname[1] == ':') + next += 2; // skip drive letter + if (*next == '/') + next++; // skip root directory + } // If we're supposed to create intermediate directories - if ( create_parents ) { - // Find the end of the initial name component - char * next = strchr(pathname,'/'); - if ( pathname[0] == '/') - next = strchr(&pathname[1],'/'); - + if (create_parents) { // Loop through the directory components until we're done - while ( next != 0 ) { + while (*next) { + next = strchr(next, '/'); *next = 0; if (!CreateDirectory(pathname, NULL)) ThrowError(std::string(pathname) + ": Can't create directory: "); - char* save = next; - next = strchr(pathname,'/'); - *save = '/'; + *next++ = '/'; + } + } else { + // Drop trailing slash. + pathname[path.size()-1] = 0; + if (!CreateDirectory(pathname, NULL)) { + ThrowError(std::string(pathname) + ": Can't create directory: "); } - } else if (!CreateDirectory(pathname, NULL)) { - ThrowError(std::string(pathname) + ": Can't create directory: "); } return true; } @@ -431,7 +478,7 @@ Path::create_file() { if (!is_file()) return false; // Create the file - HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) ThrowError(std::string(path.c_str()) + ": Can't create file: "); -- 2.11.0