From 0fefd7d03a062ba053144ee069e5bfd855593067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Obiltschnig?= Date: Thu, 2 Feb 2017 20:54:59 +0100 Subject: [PATCH] fixed GH #1523: Long path names under Windows --- Foundation/include/Poco/File.h | 13 +++++++++++ Foundation/include/Poco/File_WIN32U.h | 3 +++ Foundation/include/Poco/File_WINCE.h | 3 +++ Foundation/src/DirectoryIterator_WIN32U.cpp | 2 +- Foundation/src/DirectoryWatcher.cpp | 5 +--- Foundation/src/FileStream_WIN32.cpp | 2 +- Foundation/src/File_WIN32U.cpp | 26 +++++++++++++++++---- Foundation/src/File_WINCE.cpp | 13 +++++++---- Foundation/src/LogFile_WIN32U.cpp | 2 +- Foundation/src/Process_WIN32U.cpp | 20 ++++++++++++++-- Foundation/testsuite/src/FileTest.cpp | 25 ++++++++++++++++++++ Foundation/testsuite/src/FileTest.h | 1 + 12 files changed, 98 insertions(+), 17 deletions(-) diff --git a/Foundation/include/Poco/File.h b/Foundation/include/Poco/File.h index 00ba0a32d..1dbfe6ceb 100644 --- a/Foundation/include/Poco/File.h +++ b/Foundation/include/Poco/File.h @@ -50,6 +50,19 @@ class Path; class Foundation_API File: private FileImpl /// The File class provides methods for working with a file. + /// + /// Regarding paths passed to the various methods, note that + /// platform-specific limitations regarding maximum length + /// of the entire path and its components apply. + /// + /// On Windows, if compiled with UTF-8 support (POCO_WIN32_UTF8) + /// the implementation tries to work around the rather low + /// 260 characters MAX_PATH limit by adding the "\\?\" prefix if + /// a path is absolute and exceeds MAX_PATH characters in length. + /// Note that various limitations regarding usage of the "\\?\" + /// prefix apply in that case, e.g. the path must + /// not contain relative components ("." and "..") and must not + /// use the forward slash ("/") as directory separator. { public: typedef FileSizeImpl FileSize; diff --git a/Foundation/include/Poco/File_WIN32U.h b/Foundation/include/Poco/File_WIN32U.h index fe0c7499f..6159ac278 100644 --- a/Foundation/include/Poco/File_WIN32U.h +++ b/Foundation/include/Poco/File_WIN32U.h @@ -63,6 +63,7 @@ protected: FileSizeImpl usableSpaceImpl() const; FileSizeImpl freeSpaceImpl() const; static void handleLastErrorImpl(const std::string& path); + static void convertPath(const std::string& utf8Path, std::wstring& utf16Path); private: std::string _path; @@ -71,6 +72,8 @@ private: friend class FileHandle; friend class DirectoryIteratorImpl; friend class WindowsDirectoryWatcherStrategy; + friend class FileStreamBuf; + friend class LogFileImpl; }; diff --git a/Foundation/include/Poco/File_WINCE.h b/Foundation/include/Poco/File_WINCE.h index 9a8dd5dd1..0dfa6244b 100644 --- a/Foundation/include/Poco/File_WINCE.h +++ b/Foundation/include/Poco/File_WINCE.h @@ -63,6 +63,7 @@ protected: FileSizeImpl usableSpaceImpl() const; FileSizeImpl freeSpaceImpl() const; static void handleLastErrorImpl(const std::string& path); + static void convertPath(const std::string& utf8Path, std::wstring& utf16Path); private: std::string _path; @@ -70,6 +71,8 @@ private: friend class FileHandle; friend class DirectoryIteratorImpl; + friend class FileStreamBuf; + friend class LogFileImpl; }; diff --git a/Foundation/src/DirectoryIterator_WIN32U.cpp b/Foundation/src/DirectoryIterator_WIN32U.cpp index acd1dc2a6..be3d35a43 100644 --- a/Foundation/src/DirectoryIterator_WIN32U.cpp +++ b/Foundation/src/DirectoryIterator_WIN32U.cpp @@ -35,7 +35,7 @@ DirectoryIteratorImpl::DirectoryIteratorImpl(const std::string& path): _fh(INVAL std::string findPath = p.toString(); findPath.append("*"); std::wstring uFindPath; - UnicodeConverter::toUTF16(findPath, uFindPath); + FileImpl::convertPath(findPath, uFindPath); _fh = FindFirstFileW(uFindPath.c_str(), &_fd); if (_fh == INVALID_HANDLE_VALUE) diff --git a/Foundation/src/DirectoryWatcher.cpp b/Foundation/src/DirectoryWatcher.cpp index aaf4bccb2..590bc161a 100644 --- a/Foundation/src/DirectoryWatcher.cpp +++ b/Foundation/src/DirectoryWatcher.cpp @@ -29,9 +29,6 @@ #include "Poco/Event.h" #include "Poco/Exception.h" #include "Poco/Buffer.h" -#if defined(POCO_WIN32_UTF8) - #include "Poco/UnicodeConverter.h" -#endif #if POCO_OS == POCO_OS_LINUX #include #include @@ -200,7 +197,7 @@ public: std::string path(owner().directory().path()); #if defined(POCO_WIN32_UTF8) std::wstring upath; - Poco::UnicodeConverter::toUTF16(path.c_str(), upath); + FileImpl::convertPath(path.c_str(), upath); HANDLE hChange = FindFirstChangeNotificationW(upath.c_str(), FALSE, filter); #else HANDLE hChange = FindFirstChangeNotificationA(path.c_str(), FALSE, filter); diff --git a/Foundation/src/FileStream_WIN32.cpp b/Foundation/src/FileStream_WIN32.cpp index e75f1ee4a..55d4b97e3 100644 --- a/Foundation/src/FileStream_WIN32.cpp +++ b/Foundation/src/FileStream_WIN32.cpp @@ -68,7 +68,7 @@ void FileStreamBuf::open(const std::string& path, std::ios::openmode mode) #if defined (POCO_WIN32_UTF8) std::wstring utf16Path; - UnicodeConverter::toUTF16(path, utf16Path); + FileImpl::convertPath(path, utf16Path); _handle = CreateFileW(utf16Path.c_str(), access, shareMode, NULL, creationDisp, flags, NULL); #else _handle = CreateFileA(path.c_str(), access, shareMode, NULL, creationDisp, flags, NULL); diff --git a/Foundation/src/File_WIN32U.cpp b/Foundation/src/File_WIN32U.cpp index 370350fda..57dd82ee5 100644 --- a/Foundation/src/File_WIN32U.cpp +++ b/Foundation/src/File_WIN32U.cpp @@ -63,7 +63,7 @@ FileImpl::FileImpl(const std::string& path): _path(path) { _path.resize(n - 1); } - UnicodeConverter::toUTF16(_path, _upath); + convertPath(_path, _upath); } @@ -87,7 +87,7 @@ void FileImpl::setPathImpl(const std::string& path) { _path.resize(n - 1); } - UnicodeConverter::toUTF16(_path, _upath); + convertPath(_path, _upath); } @@ -293,7 +293,7 @@ void FileImpl::copyToImpl(const std::string& path) const poco_assert (!_path.empty()); std::wstring upath; - UnicodeConverter::toUTF16(path, upath); + convertPath(path, upath); if (CopyFileW(_upath.c_str(), upath.c_str(), FALSE) == 0) handleLastErrorImpl(_path); } @@ -304,7 +304,7 @@ void FileImpl::renameToImpl(const std::string& path) poco_assert (!_path.empty()); std::wstring upath; - UnicodeConverter::toUTF16(path, upath); + convertPath(path, upath); if (MoveFileExW(_upath.c_str(), upath.c_str(), MOVEFILE_REPLACE_EXISTING) == 0) handleLastErrorImpl(_path); } @@ -439,4 +439,22 @@ void FileImpl::handleLastErrorImpl(const std::string& path) } +void FileImpl::convertPath(const std::string& utf8Path, std::wstring& utf16Path) +{ + UnicodeConverter::toUTF16(utf8Path, utf16Path); + if (utf16Path.size() > MAX_PATH - 12) // Note: CreateDirectory has a limit of MAX_PATH - 12 (room for 8.3 file name) + { + if (utf16Path[0] == '\\' || utf16Path[1] == ':') + { + if (utf16Path.compare(0, 4, L"\\\\?\\", 4) != 0) + { + if (utf16Path[1] == '\\') + utf16Path.insert(0, L"\\\\?\\UNC\\", 8); + else + utf16Path.insert(0, L"\\\\?\\", 4); + } + } + } +} + } // namespace Poco diff --git a/Foundation/src/File_WINCE.cpp b/Foundation/src/File_WINCE.cpp index 5f5ddc0b6..3dbc72992 100644 --- a/Foundation/src/File_WINCE.cpp +++ b/Foundation/src/File_WINCE.cpp @@ -64,7 +64,7 @@ FileImpl::FileImpl(const std::string& path): _path(path) { _path.resize(n - 1); } - UnicodeConverter::toUTF16(_path, _upath); + convertPath(_path, _upath); } @@ -88,7 +88,7 @@ void FileImpl::setPathImpl(const std::string& path) { _path.resize(n - 1); } - UnicodeConverter::toUTF16(_path, _upath); + convertPath(_path, _upath); } @@ -284,7 +284,7 @@ void FileImpl::copyToImpl(const std::string& path) const poco_assert (!_path.empty()); std::wstring upath; - UnicodeConverter::toUTF16(path, upath); + convertPath(path, upath); if (CopyFileW(_upath.c_str(), upath.c_str(), FALSE) == 0) handleLastErrorImpl(_path); } @@ -295,7 +295,7 @@ void FileImpl::renameToImpl(const std::string& path) poco_assert (!_path.empty()); std::wstring upath; - UnicodeConverter::toUTF16(path, upath); + convertPath(path, upath); if (MoveFileW(_upath.c_str(), upath.c_str()) == 0) handleLastErrorImpl(_path); } @@ -429,4 +429,9 @@ void FileImpl::handleLastErrorImpl(const std::string& path) } +void FileImpl::convertPath(const std::string& utf8Path, std::wstring& utf16Path) +{ + UnicodeConverter::toUTF16(utf8Path, utf16Path); +} + } // namespace Poco diff --git a/Foundation/src/LogFile_WIN32U.cpp b/Foundation/src/LogFile_WIN32U.cpp index 454dedc0e..972b7f240 100644 --- a/Foundation/src/LogFile_WIN32U.cpp +++ b/Foundation/src/LogFile_WIN32U.cpp @@ -90,7 +90,7 @@ const std::string& LogFileImpl::pathImpl() const void LogFileImpl::createFile() { std::wstring upath; - UnicodeConverter::toUTF16(_path, upath); + FileImpl::convertPath(_path, upath); _hFile = CreateFileW(upath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (_hFile == INVALID_HANDLE_VALUE) throw OpenFileException(_path); diff --git a/Foundation/src/Process_WIN32U.cpp b/Foundation/src/Process_WIN32U.cpp index b8e7f1c0b..8176bac2f 100644 --- a/Foundation/src/Process_WIN32U.cpp +++ b/Foundation/src/Process_WIN32U.cpp @@ -20,6 +20,9 @@ #include "Poco/NamedEvent.h" #include "Poco/UnicodeConverter.h" #include "Poco/Pipe.h" +#include "Poco/File.h" +#include "Poco/Path.h" +#include "Poco/String.h" namespace Poco { @@ -164,7 +167,7 @@ static std::string escapeArg(const std::string& arg) ProcessHandleImpl* ProcessImpl::launchImpl(const std::string& command, const ArgsImpl& args, const std::string& initialDirectory, Pipe* inPipe, Pipe* outPipe, Pipe* errPipe, const EnvImpl& env) { - std::string commandLine = command; + std::string commandLine = escapeArg(command); for (ArgsImpl::const_iterator it = args.begin(); it != args.end(); ++it) { commandLine.append(" "); @@ -174,6 +177,19 @@ ProcessHandleImpl* ProcessImpl::launchImpl(const std::string& command, const Arg std::wstring ucommandLine; UnicodeConverter::toUTF16(commandLine, ucommandLine); + const wchar_t* applicationName = 0; + std::wstring uapplicationName; + if (command.size() > MAX_PATH) + { + Poco::Path p(command); + if (p.isAbsolute()) + { + UnicodeConverter::toUTF16(command, uapplicationName); + if (p.getExtension().empty()) uapplicationName += L".EXE"; + applicationName = uapplicationName.c_str(); + } + } + STARTUPINFOW startupInfo; GetStartupInfoW(&startupInfo); // take defaults from current process startupInfo.cb = sizeof(STARTUPINFOW); @@ -253,7 +269,7 @@ ProcessHandleImpl* ProcessImpl::launchImpl(const std::string& command, const Arg PROCESS_INFORMATION processInfo; DWORD creationFlags = GetConsoleWindow() ? 0 : CREATE_NO_WINDOW; BOOL rc = CreateProcessW( - NULL, // applicationName + applicationName, const_cast(ucommandLine.c_str()), NULL, // processAttributes NULL, // threadAttributes diff --git a/Foundation/testsuite/src/FileTest.cpp b/Foundation/testsuite/src/FileTest.cpp index 9afa18344..db81db85a 100644 --- a/Foundation/testsuite/src/FileTest.cpp +++ b/Foundation/testsuite/src/FileTest.cpp @@ -524,6 +524,30 @@ void FileTest::testRename() } +void FileTest::testLongPath() +{ +#if defined(_WIN32) && defined(POCO_WIN32_UTF8) && !defined(_WIN32_WCE) + Poco::Path p("longpathtest"); + p.makeAbsolute(); + std::string longpath(p.toString()); + while (longpath.size() < MAX_PATH*4) + { + longpath.append("\\"); + longpath.append(64, 'x'); + } + + Poco::File d(longpath); + d.createDirectories(); + + assert (d.exists()); + assert (d.isDirectory()); + + Poco::File f(p.toString()); + f.remove(true); +#endif +} + + void FileTest::setUp() { File f("testfile.dat"); @@ -568,6 +592,7 @@ CppUnit::Test* FileTest::suite() CppUnit_addTest(pSuite, FileTest, testCopyDirectory); CppUnit_addTest(pSuite, FileTest, testRename); CppUnit_addTest(pSuite, FileTest, testRootDir); + CppUnit_addTest(pSuite, FileTest, testLongPath); return pSuite; } diff --git a/Foundation/testsuite/src/FileTest.h b/Foundation/testsuite/src/FileTest.h index 859bc137f..b475de439 100644 --- a/Foundation/testsuite/src/FileTest.h +++ b/Foundation/testsuite/src/FileTest.h @@ -40,6 +40,7 @@ public: void testCopyDirectory(); void testRename(); void testRootDir(); + void testLongPath(); void setUp(); void tearDown();