Merge pull request #3016 from nextcloud/fix_for_win_createfile_long_path

Fix for Windows CreateFile long path
This commit is contained in:
allexzander 2021-03-18 12:34:22 +02:00 committed by GitHub
commit 6abb0b2184
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 21 deletions

View file

@ -52,7 +52,7 @@ struct csync_vio_handle_t {
QString path; // Always ends with '\'
};
static int _csync_vio_local_stat_mb(const mbchar_t *uri, csync_file_stat_t *buf);
static int _csync_vio_local_stat_mb(const QString &path, csync_file_stat_t *buf);
csync_vio_handle_t *csync_vio_local_opendir(const QString &name) {
@ -174,12 +174,12 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *h
file_stat->size = (handle->ffd.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + handle->ffd.nFileSizeLow;
file_stat->modtime = FileTimeToUnixTime(&handle->ffd.ftLastWriteTime, &rem);
std::wstring fullPath;
QString fullPath;
fullPath.reserve(handle->path.size() + std::wcslen(handle->ffd.cFileName));
fullPath += handle->path.toStdWString(); // path always ends with '\', by construction
fullPath += handle->ffd.cFileName;
fullPath += handle->path; // path always ends with '\', by construction
fullPath += QString::fromWCharArray(handle->ffd.cFileName);
if (_csync_vio_local_stat_mb(fullPath.data(), file_stat.get()) < 0) {
if (_csync_vio_local_stat_mb(fullPath, file_stat.get()) < 0) {
// Will get excluded by _csync_detect_update.
file_stat->type = ItemTypeSkip;
}
@ -190,12 +190,11 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *h
int csync_vio_local_stat(const QString &uri, csync_file_stat_t *buf)
{
const std::wstring wuri = uri.toStdWString();
int rc = _csync_vio_local_stat_mb(wuri.data(), buf);
int rc = _csync_vio_local_stat_mb(uri, buf);
return rc;
}
static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf)
static int _csync_vio_local_stat_mb(const QString &path, csync_file_stat_t *buf)
{
/* Almost nothing to do since csync_vio_local_readdir already filled up most of the information
But we still need to fetch the file ID.
@ -206,18 +205,20 @@ static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf
BY_HANDLE_FILE_INFORMATION fileInfo;
ULARGE_INTEGER FileIndex;
h = CreateFileW( wuri, 0, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
const auto longPath = OCC::FileSystem::longWinPath(path);
h = CreateFileW(longPath.toStdWString().data(), 0, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
nullptr );
if( h == INVALID_HANDLE_VALUE ) {
qCCritical(lcCSyncVIOLocal, "CreateFileW failed on %ls", wuri);
qCCritical(lcCSyncVIOLocal) << "CreateFileW failed on" << longPath;
errno = GetLastError();
return -1;
}
if(!GetFileInformationByHandle( h, &fileInfo ) ) {
qCCritical(lcCSyncVIOLocal, "GetFileInformationByHandle failed on %ls", wuri);
qCCritical(lcCSyncVIOLocal) << "GetFileInformationByHandle failed on" << longPath;
errno = GetLastError();
CloseHandle(h);
return -1;

View file

@ -15,6 +15,7 @@
#include "cfapiwrapper.h"
#include "common/utility.h"
#include "common/filesystembase.h"
#include "hydrationjob.h"
#include "vfs_cfapi.h"
@ -370,6 +371,10 @@ bool OCC::CfApiWrapper::isSparseFile(const QString &path)
OCC::CfApiWrapper::FileHandle OCC::CfApiWrapper::handleForPath(const QString &path)
{
if (path.isEmpty()) {
return {};
}
if (QFileInfo(path).isDir()) {
HANDLE handle = nullptr;
const qint64 openResult = CfOpenFileWithOplock(path.toStdWString().data(), CF_OPEN_FILE_FLAG_NONE, &handle);
@ -377,10 +382,13 @@ OCC::CfApiWrapper::FileHandle OCC::CfApiWrapper::handleForPath(const QString &pa
return {handle, [](HANDLE h) { CfCloseHandle(h); }};
}
} else {
const auto handle = CreateFile(path.toStdWString().data(), 0, 0, nullptr,
const auto longpath = OCC::FileSystem::longWinPath(path);
const auto handle = CreateFile(longpath.toStdWString().data(), 0, 0, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle != INVALID_HANDLE_VALUE) {
return {handle, [](HANDLE h) { CloseHandle(h); }};
} else {
qCCritical(lcCfApiWrapper) << "Could not CreateFile for longpath:" << longpath << "with error:" << GetLastError();
}
}
@ -403,7 +411,7 @@ OCC::CfApiWrapper::PlaceHolderInfo OCC::CfApiWrapper::findPlaceholderInfo(const
}
}
OCC::Result<void, QString> OCC::CfApiWrapper::setPinState(const FileHandle &handle, PinState state, SetPinRecurseMode mode)
OCC::Result<void, QString> OCC::CfApiWrapper::setPinState(const FileHandle &handle, OCC::PinStateEnums::PinState state, SetPinRecurseMode mode)
{
const auto cfState = pinStateToCfPinState(state);
const auto flags = pinRecurseModeToCfSetPinFlags(mode);
@ -413,7 +421,7 @@ OCC::Result<void, QString> OCC::CfApiWrapper::setPinState(const FileHandle &hand
return {};
} else {
qCWarning(lcCfApiWrapper) << "Couldn't set pin state" << state << "for" << pathForHandle(handle) << "with recurse mode" << mode << ":" << _com_error(result).ErrorMessage();
return "Couldn't set pin state";
return { "Couldn't set pin state" };
}
}
@ -448,7 +456,7 @@ OCC::Result<void, QString> OCC::CfApiWrapper::createPlaceholderInfo(const QStrin
const qint64 result = CfCreatePlaceholders(localBasePath.data(), &cloudEntry, 1, CF_CREATE_FLAG_NONE, nullptr);
if (result != S_OK) {
qCWarning(lcCfApiWrapper) << "Couldn't create placeholder info for" << path << ":" << _com_error(result).ErrorMessage();
return "Couldn't create placeholder info";
return { "Couldn't create placeholder info" };
}
const auto parentHandle = handleForPath(QDir::toNativeSeparators(QFileInfo(path).absolutePath()));
@ -457,7 +465,7 @@ OCC::Result<void, QString> OCC::CfApiWrapper::createPlaceholderInfo(const QStrin
const auto handle = handleForPath(path);
if (!setPinState(handle, cfPinStateToPinState(state), NoRecurse)) {
return "Couldn't set the default inherit pin state";
return { "Couldn't set the default inherit pin state" };
}
return {};
@ -470,7 +478,7 @@ OCC::Result<void, QString> OCC::CfApiWrapper::updatePlaceholderInfo(const FileHa
const auto info = replacesPath.isEmpty() ? findPlaceholderInfo(handle)
: findPlaceholderInfo(handleForPath(replacesPath));
if (!info) {
return "Can't update non existing placeholder info";
return { "Can't update non existing placeholder info" };
}
const auto previousPinState = cfPinStateToPinState(info->PinState);
@ -490,12 +498,12 @@ OCC::Result<void, QString> OCC::CfApiWrapper::updatePlaceholderInfo(const FileHa
if (result != S_OK) {
qCWarning(lcCfApiWrapper) << "Couldn't update placeholder info for" << pathForHandle(handle) << ":" << _com_error(result).ErrorMessage();
return "Couldn't update placeholder info";
return { "Couldn't update placeholder info" };
}
// Pin state tends to be lost on updates, so restore it every time
if (!setPinState(handle, previousPinState, NoRecurse)) {
return "Couldn't restore pin state";
return { "Couldn't restore pin state" };
}
return {};
@ -503,6 +511,9 @@ OCC::Result<void, QString> OCC::CfApiWrapper::updatePlaceholderInfo(const FileHa
OCC::Result<void, QString> OCC::CfApiWrapper::convertToPlaceholder(const FileHandle &handle, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath)
{
Q_UNUSED(modtime);
Q_UNUSED(size);
Q_ASSERT(handle);
const auto fileIdentity = QString::fromUtf8(fileId).toStdWString();
@ -511,7 +522,7 @@ OCC::Result<void, QString> OCC::CfApiWrapper::convertToPlaceholder(const FileHan
Q_ASSERT(result == S_OK);
if (result != S_OK) {
qCCritical(lcCfApiWrapper) << "Couldn't convert to placeholder" << pathForHandle(handle) << ":" << _com_error(result).ErrorMessage();
return "Couldn't convert to placeholder";
return { "Couldn't convert to placeholder" };
}
const auto originalHandle = handleForPath(replacesPath);

View file

@ -375,7 +375,8 @@ VfsCfApi::HydratationAndPinStates VfsCfApi::computeRecursiveHydrationAndPinState
return currentState;
}
const auto path = folderPath + '/' + name;
// if the folderPath.isEmpty() we don't want to end up having path "/example.file" because this will lead to double slash later, when appending to "SyncFolder/"
const auto path = folderPath.isEmpty() ? name : folderPath + '/' + name;
const auto states = computeRecursiveHydrationAndPinStates(path, currentState.pinState);
return HydratationAndPinStates {
states.pinState,