fixed GH #1523: Long path names under Windows

This commit is contained in:
Günter Obiltschnig 2017-02-02 20:54:59 +01:00
parent d5ce48138c
commit 0fefd7d03a
12 changed files with 98 additions and 17 deletions

View File

@ -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;

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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)

View File

@ -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 <sys/inotify.h>
#include <sys/select.h>
@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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<wchar_t*>(ucommandLine.c_str()),
NULL, // processAttributes
NULL, // threadAttributes

View File

@ -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;
}

View File

@ -40,6 +40,7 @@ public:
void testCopyDirectory();
void testRename();
void testRootDir();
void testLongPath();
void setUp();
void tearDown();