mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-29 20:28:57 +03:00
Move non-csync-dependent parts of FileSystem to src/common
This keep the csync-dependent parts in src/libsync, slitting the namespace over two files. This will allow moving SyncJournalDB to src/common as well.
This commit is contained in:
parent
dc3c3195b5
commit
4c2e078eac
13 changed files with 724 additions and 710 deletions
|
@ -2,5 +2,6 @@
|
||||||
# Essentially they could be in the same directory but are separate to
|
# Essentially they could be in the same directory but are separate to
|
||||||
# help keep track of the different code licenses.
|
# help keep track of the different code licenses.
|
||||||
set(common_SOURCES
|
set(common_SOURCES
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/filesystembase.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/utility.cpp
|
${CMAKE_CURRENT_LIST_DIR}/utility.cpp
|
||||||
)
|
)
|
||||||
|
|
488
src/common/filesystembase.cpp
Normal file
488
src/common/filesystembase.cpp
Normal file
|
@ -0,0 +1,488 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "filesystembase.h"
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#ifdef ZLIB_FOUND
|
||||||
|
#include <zlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <windef.h>
|
||||||
|
#include <winbase.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <io.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(lcFileSystem, "sync.filesystem", QtInfoMsg)
|
||||||
|
|
||||||
|
QString FileSystem::longWinPath(const QString &inpath)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
return pathtoUNC(inpath);
|
||||||
|
#else
|
||||||
|
return inpath;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystem::setFileHidden(const QString &filename, bool hidden)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
QString fName = longWinPath(filename);
|
||||||
|
DWORD dwAttrs;
|
||||||
|
|
||||||
|
dwAttrs = GetFileAttributesW((wchar_t *)fName.utf16());
|
||||||
|
|
||||||
|
if (dwAttrs != INVALID_FILE_ATTRIBUTES) {
|
||||||
|
if (hidden && !(dwAttrs & FILE_ATTRIBUTE_HIDDEN)) {
|
||||||
|
SetFileAttributesW((wchar_t *)fName.utf16(), dwAttrs | FILE_ATTRIBUTE_HIDDEN);
|
||||||
|
} else if (!hidden && (dwAttrs & FILE_ATTRIBUTE_HIDDEN)) {
|
||||||
|
SetFileAttributesW((wchar_t *)fName.utf16(), dwAttrs & ~FILE_ATTRIBUTE_HIDDEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Q_UNUSED(filename);
|
||||||
|
Q_UNUSED(hidden);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static QFile::Permissions getDefaultWritePermissions()
|
||||||
|
{
|
||||||
|
QFile::Permissions result = QFile::WriteUser;
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
mode_t mask = umask(0);
|
||||||
|
umask(mask);
|
||||||
|
if (!(mask & S_IWGRP)) {
|
||||||
|
result |= QFile::WriteGroup;
|
||||||
|
}
|
||||||
|
if (!(mask & S_IWOTH)) {
|
||||||
|
result |= QFile::WriteOther;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystem::setFileReadOnly(const QString &filename, bool readonly)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
QFile::Permissions permissions = file.permissions();
|
||||||
|
|
||||||
|
QFile::Permissions allWritePermissions =
|
||||||
|
QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther | QFile::WriteOwner;
|
||||||
|
static QFile::Permissions defaultWritePermissions = getDefaultWritePermissions();
|
||||||
|
|
||||||
|
permissions &= ~allWritePermissions;
|
||||||
|
if (!readonly) {
|
||||||
|
permissions |= defaultWritePermissions;
|
||||||
|
}
|
||||||
|
file.setPermissions(permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystem::setFolderMinimumPermissions(const QString &filename)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
QFile::Permissions perm = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
|
||||||
|
QFile file(filename);
|
||||||
|
file.setPermissions(perm);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(filename);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FileSystem::setFileReadOnlyWeak(const QString &filename, bool readonly)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
QFile::Permissions permissions = file.permissions();
|
||||||
|
|
||||||
|
if (!readonly && (permissions & QFile::WriteOwner)) {
|
||||||
|
return; // already writable enough
|
||||||
|
}
|
||||||
|
|
||||||
|
setFileReadOnly(filename, readonly);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::rename(const QString &originFileName,
|
||||||
|
const QString &destinationFileName,
|
||||||
|
QString *errorString)
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
QString error;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QString orig = longWinPath(originFileName);
|
||||||
|
QString dest = longWinPath(destinationFileName);
|
||||||
|
|
||||||
|
if (isLnkFile(originFileName) || isLnkFile(destinationFileName)) {
|
||||||
|
success = MoveFileEx((wchar_t *)orig.utf16(),
|
||||||
|
(wchar_t *)dest.utf16(),
|
||||||
|
MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH);
|
||||||
|
if (!success) {
|
||||||
|
wchar_t *string = 0;
|
||||||
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
||||||
|
NULL, ::GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPWSTR)&string, 0, NULL);
|
||||||
|
|
||||||
|
error = QString::fromWCharArray(string);
|
||||||
|
LocalFree((HLOCAL)string);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
QFile orig(originFileName);
|
||||||
|
success = orig.rename(destinationFileName);
|
||||||
|
if (!success) {
|
||||||
|
error = orig.errorString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
qCWarning(lcFileSystem) << "Error renaming file" << originFileName
|
||||||
|
<< "to" << destinationFileName
|
||||||
|
<< "failed: " << error;
|
||||||
|
if (errorString) {
|
||||||
|
*errorString = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::uncheckedRenameReplace(const QString &originFileName,
|
||||||
|
const QString &destinationFileName,
|
||||||
|
QString *errorString)
|
||||||
|
{
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
bool success;
|
||||||
|
QFile orig(originFileName);
|
||||||
|
// We want a rename that also overwites. QFile::rename does not overwite.
|
||||||
|
// Qt 5.1 has QSaveFile::renameOverwrite we could use.
|
||||||
|
// ### FIXME
|
||||||
|
success = true;
|
||||||
|
bool destExists = fileExists(destinationFileName);
|
||||||
|
if (destExists && !QFile::remove(destinationFileName)) {
|
||||||
|
*errorString = orig.errorString();
|
||||||
|
qCWarning(lcFileSystem) << "Target file could not be removed.";
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
success = orig.rename(destinationFileName);
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
|
*errorString = orig.errorString();
|
||||||
|
qCWarning(lcFileSystem) << "Renaming temp file to final failed: " << *errorString;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else //Q_OS_WIN
|
||||||
|
// You can not overwrite a read-only file on windows.
|
||||||
|
if (!QFileInfo(destinationFileName).isWritable()) {
|
||||||
|
setFileReadOnly(destinationFileName, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL ok;
|
||||||
|
QString orig = longWinPath(originFileName);
|
||||||
|
QString dest = longWinPath(destinationFileName);
|
||||||
|
|
||||||
|
ok = MoveFileEx((wchar_t *)orig.utf16(),
|
||||||
|
(wchar_t *)dest.utf16(),
|
||||||
|
MOVEFILE_REPLACE_EXISTING + MOVEFILE_COPY_ALLOWED + MOVEFILE_WRITE_THROUGH);
|
||||||
|
if (!ok) {
|
||||||
|
wchar_t *string = 0;
|
||||||
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
||||||
|
NULL, ::GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPWSTR)&string, 0, NULL);
|
||||||
|
|
||||||
|
*errorString = QString::fromWCharArray(string);
|
||||||
|
qCWarning(lcFileSystem) << "Renaming temp file to final failed: " << *errorString;
|
||||||
|
LocalFree((HLOCAL)string);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::openAndSeekFileSharedRead(QFile *file, QString *errorOrNull, qint64 seek)
|
||||||
|
{
|
||||||
|
QString errorDummy;
|
||||||
|
// avoid many if (errorOrNull) later.
|
||||||
|
QString &error = errorOrNull ? *errorOrNull : errorDummy;
|
||||||
|
error.clear();
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
//
|
||||||
|
// The following code is adapted from Qt's QFSFileEnginePrivate::nativeOpen()
|
||||||
|
// by including the FILE_SHARE_DELETE share mode.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Enable full sharing.
|
||||||
|
DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||||
|
|
||||||
|
int accessRights = GENERIC_READ;
|
||||||
|
DWORD creationDisp = OPEN_EXISTING;
|
||||||
|
|
||||||
|
// Create the file handle.
|
||||||
|
SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
|
||||||
|
QString fName = longWinPath(file->fileName());
|
||||||
|
|
||||||
|
HANDLE fileHandle = CreateFileW(
|
||||||
|
(const wchar_t *)fName.utf16(),
|
||||||
|
accessRights,
|
||||||
|
shareMode,
|
||||||
|
&securityAtts,
|
||||||
|
creationDisp,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
// Bail out on error.
|
||||||
|
if (fileHandle == INVALID_HANDLE_VALUE) {
|
||||||
|
error = qt_error_string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the HANDLE to an fd and pass it to QFile's foreign-open
|
||||||
|
// function. The fd owns the handle, so when QFile later closes
|
||||||
|
// the fd the handle will be closed too.
|
||||||
|
int fd = _open_osfhandle((intptr_t)fileHandle, _O_RDONLY);
|
||||||
|
if (fd == -1) {
|
||||||
|
error = "could not make fd from handle";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!file->open(fd, QIODevice::ReadOnly, QFile::AutoCloseHandle)) {
|
||||||
|
error = file->errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek to the right spot
|
||||||
|
LARGE_INTEGER *li = reinterpret_cast<LARGE_INTEGER *>(&seek);
|
||||||
|
DWORD newFilePointer = SetFilePointer(fileHandle, li->LowPart, &li->HighPart, FILE_BEGIN);
|
||||||
|
if (newFilePointer == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
|
||||||
|
error = qt_error_string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
if (!file->open(QFile::ReadOnly)) {
|
||||||
|
error = file->errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!file->seek(seek)) {
|
||||||
|
error = file->errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
static bool fileExistsWin(const QString &filename)
|
||||||
|
{
|
||||||
|
WIN32_FIND_DATA FindFileData;
|
||||||
|
HANDLE hFind;
|
||||||
|
QString fName = FileSystem::longWinPath(filename);
|
||||||
|
|
||||||
|
hFind = FindFirstFileW((wchar_t *)fName.utf16(), &FindFileData);
|
||||||
|
if (hFind == INVALID_HANDLE_VALUE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FindClose(hFind);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool FileSystem::fileExists(const QString &filename, const QFileInfo &fileInfo)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
if (isLnkFile(filename)) {
|
||||||
|
// Use a native check.
|
||||||
|
return fileExistsWin(filename);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool re = fileInfo.exists();
|
||||||
|
// if the filename is different from the filename in fileInfo, the fileInfo is
|
||||||
|
// not valid. There needs to be one initialised here. Otherwise the incoming
|
||||||
|
// fileInfo is re-used.
|
||||||
|
if (fileInfo.filePath() != filename) {
|
||||||
|
QFileInfo myFI(filename);
|
||||||
|
re = myFI.exists();
|
||||||
|
}
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QString FileSystem::fileSystemForPath(const QString &path)
|
||||||
|
{
|
||||||
|
// See also QStorageInfo (Qt >=5.4) and GetVolumeInformationByHandleW (>= Vista)
|
||||||
|
QString drive = path.left(2);
|
||||||
|
if (!drive.endsWith(":"))
|
||||||
|
return QString();
|
||||||
|
drive.append('\\');
|
||||||
|
|
||||||
|
const size_t fileSystemBufferSize = 4096;
|
||||||
|
TCHAR fileSystemBuffer[fileSystemBufferSize];
|
||||||
|
|
||||||
|
if (!GetVolumeInformationW(
|
||||||
|
reinterpret_cast<LPCWSTR>(drive.utf16()),
|
||||||
|
NULL, 0,
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
fileSystemBuffer, fileSystemBufferSize)) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return QString::fromUtf16(reinterpret_cast<const ushort *>(fileSystemBuffer));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BUFSIZE qint64(500 * 1024) // 500 KiB
|
||||||
|
|
||||||
|
static QByteArray readToCrypto(const QString &filename, QCryptographicHash::Algorithm algo)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
const qint64 bufSize = qMin(BUFSIZE, file.size() + 1);
|
||||||
|
QByteArray buf(bufSize, Qt::Uninitialized);
|
||||||
|
QByteArray arr;
|
||||||
|
QCryptographicHash crypto(algo);
|
||||||
|
|
||||||
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
|
qint64 size;
|
||||||
|
while (!file.atEnd()) {
|
||||||
|
size = file.read(buf.data(), bufSize);
|
||||||
|
if (size > 0) {
|
||||||
|
crypto.addData(buf.data(), size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arr = crypto.result().toHex();
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray FileSystem::calcMd5(const QString &filename)
|
||||||
|
{
|
||||||
|
return readToCrypto(filename, QCryptographicHash::Md5);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray FileSystem::calcSha1(const QString &filename)
|
||||||
|
{
|
||||||
|
return readToCrypto(filename, QCryptographicHash::Sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ZLIB_FOUND
|
||||||
|
QByteArray FileSystem::calcAdler32(const QString &filename)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
const qint64 bufSize = qMin(BUFSIZE, file.size() + 1);
|
||||||
|
QByteArray buf(bufSize, Qt::Uninitialized);
|
||||||
|
|
||||||
|
unsigned int adler = adler32(0L, Z_NULL, 0);
|
||||||
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
|
qint64 size;
|
||||||
|
while (!file.atEnd()) {
|
||||||
|
size = file.read(buf.data(), bufSize);
|
||||||
|
if (size > 0)
|
||||||
|
adler = adler32(adler, (const Bytef *)buf.data(), size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QByteArray::number(adler, 16);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QString FileSystem::makeConflictFileName(const QString &fn, const QDateTime &dt)
|
||||||
|
{
|
||||||
|
QString conflictFileName(fn);
|
||||||
|
// Add _conflict-XXXX before the extension.
|
||||||
|
int dotLocation = conflictFileName.lastIndexOf('.');
|
||||||
|
// If no extension, add it at the end (take care of cases like foo/.hidden or foo.bar/file)
|
||||||
|
if (dotLocation <= conflictFileName.lastIndexOf('/') + 1) {
|
||||||
|
dotLocation = conflictFileName.size();
|
||||||
|
}
|
||||||
|
QString timeString = dt.toString("yyyyMMdd-hhmmss");
|
||||||
|
|
||||||
|
// Additional marker
|
||||||
|
QByteArray conflictFileUserName = qgetenv("CSYNC_CONFLICT_FILE_USERNAME");
|
||||||
|
if (conflictFileUserName.isEmpty())
|
||||||
|
conflictFileName.insert(dotLocation, "_conflict-" + timeString);
|
||||||
|
else
|
||||||
|
conflictFileName.insert(dotLocation, "_conflict_" + QString::fromUtf8(conflictFileUserName) + "-" + timeString);
|
||||||
|
|
||||||
|
return conflictFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::remove(const QString &fileName, QString *errorString)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// You cannot delete a read-only file on windows, but we want to
|
||||||
|
// allow that.
|
||||||
|
if (!QFileInfo(fileName).isWritable()) {
|
||||||
|
setFileReadOnly(fileName, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
QFile f(fileName);
|
||||||
|
if (!f.remove()) {
|
||||||
|
if (errorString) {
|
||||||
|
*errorString = f.errorString();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::isFileLocked(const QString &fileName)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
const wchar_t *wuri = reinterpret_cast<const wchar_t *>(fileName.utf16());
|
||||||
|
// Check if file exists
|
||||||
|
DWORD attr = GetFileAttributesW(wuri);
|
||||||
|
if (attr != INVALID_FILE_ATTRIBUTES) {
|
||||||
|
// Try to open the file with as much access as possible..
|
||||||
|
HANDLE win_h = CreateFileW(
|
||||||
|
wuri,
|
||||||
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (win_h == INVALID_HANDLE_VALUE) {
|
||||||
|
/* could not be opened, so locked? */
|
||||||
|
/* 32 == ERROR_SHARING_VIOLATION */
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
CloseHandle(win_h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Q_UNUSED(fileName);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::isLnkFile(const QString &filename)
|
||||||
|
{
|
||||||
|
return filename.endsWith(".lnk");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OCC
|
189
src/common/filesystembase.h
Normal file
189
src/common/filesystembase.h
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Olivier Goffart <ogoffart@owncloud.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <ctime>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
#include <ocsynclib.h>
|
||||||
|
|
||||||
|
class QFile;
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
OCSYNC_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcFileSystem)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \addtogroup libsync
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This file contains file system helper
|
||||||
|
*/
|
||||||
|
namespace FileSystem {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mark the file as hidden (only has effects on windows)
|
||||||
|
*/
|
||||||
|
void OCSYNC_EXPORT setFileHidden(const QString &filename, bool hidden);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Marks the file as read-only.
|
||||||
|
*
|
||||||
|
* On linux this either revokes all 'w' permissions or restores permissions
|
||||||
|
* according to the umask.
|
||||||
|
*/
|
||||||
|
void OCSYNC_EXPORT setFileReadOnly(const QString &filename, bool readonly);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Marks the file as read-only.
|
||||||
|
*
|
||||||
|
* It's like setFileReadOnly(), but weaker: if readonly is false and the user
|
||||||
|
* already has write permissions, no change to the permissions is made.
|
||||||
|
*
|
||||||
|
* This means that it will preserve explicitly set rw-r--r-- permissions even
|
||||||
|
* when the umask is 0002. (setFileReadOnly() would adjust to rw-rw-r--)
|
||||||
|
*/
|
||||||
|
void OCSYNC_EXPORT setFileReadOnlyWeak(const QString &filename, bool readonly);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Try to set permissions so that other users on the local machine can not
|
||||||
|
* go into the folder.
|
||||||
|
*/
|
||||||
|
void OCSYNC_EXPORT setFolderMinimumPermissions(const QString &filename);
|
||||||
|
|
||||||
|
/** convert a "normal" windows path into a path that can be 32k chars long. */
|
||||||
|
QString OCSYNC_EXPORT longWinPath(const QString &inpath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether a file exists.
|
||||||
|
*
|
||||||
|
* Use this over QFileInfo::exists() and QFile::exists() to avoid bugs with lnk
|
||||||
|
* files, see above.
|
||||||
|
*/
|
||||||
|
bool OCSYNC_EXPORT fileExists(const QString &filename, const QFileInfo & = QFileInfo());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rename the file \a originFileName to \a destinationFileName.
|
||||||
|
*
|
||||||
|
* It behaves as QFile::rename() but handles .lnk files correctly on Windows.
|
||||||
|
*/
|
||||||
|
bool OCSYNC_EXPORT rename(const QString &originFileName,
|
||||||
|
const QString &destinationFileName,
|
||||||
|
QString *errorString = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename the file \a originFileName to \a destinationFileName, and
|
||||||
|
* overwrite the destination if it already exists - without extra checks.
|
||||||
|
*/
|
||||||
|
bool OCSYNC_EXPORT uncheckedRenameReplace(const QString &originFileName,
|
||||||
|
const QString &destinationFileName,
|
||||||
|
QString *errorString);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a file.
|
||||||
|
*
|
||||||
|
* Equivalent to QFile::remove(), except on Windows, where it will also
|
||||||
|
* successfully remove read-only files.
|
||||||
|
*/
|
||||||
|
bool OCSYNC_EXPORT remove(const QString &fileName, QString *errorString = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement for QFile::open(ReadOnly) followed by a seek().
|
||||||
|
* This version sets a more permissive sharing mode on Windows.
|
||||||
|
*
|
||||||
|
* Warning: The resulting file may have an empty fileName and be unsuitable for use
|
||||||
|
* with QFileInfo! Calling seek() on the QFile with >32bit signed values will fail!
|
||||||
|
*/
|
||||||
|
bool OCSYNC_EXPORT openAndSeekFileSharedRead(QFile *file, QString *error, qint64 seek);
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
/**
|
||||||
|
* Returns the file system used at the given path.
|
||||||
|
*/
|
||||||
|
QString fileSystemForPath(const QString &path);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QByteArray OCSYNC_EXPORT calcMd5(const QString &fileName);
|
||||||
|
QByteArray OCSYNC_EXPORT calcSha1(const QString &fileName);
|
||||||
|
#ifdef ZLIB_FOUND
|
||||||
|
QByteArray OCSYNC_EXPORT calcAdler32(const QString &fileName);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a file name based on \a fn that's suitable for a conflict.
|
||||||
|
*/
|
||||||
|
QString OCSYNC_EXPORT makeConflictFileName(const QString &fn, const QDateTime &dt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when a file is locked. (Windows only)
|
||||||
|
*/
|
||||||
|
bool OCSYNC_EXPORT isFileLocked(const QString &fileName);
|
||||||
|
|
||||||
|
bool OCSYNC_EXPORT isLnkFile(const QString &filename);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function takes a path and converts it to a UNC representation of the
|
||||||
|
* string. That means that it prepends a \\?\ (unless already UNC) and converts
|
||||||
|
* all slashes to backslashes.
|
||||||
|
*
|
||||||
|
* Note the following:
|
||||||
|
* - The string must be absolute.
|
||||||
|
* - it needs to contain a drive character to be a valid UNC
|
||||||
|
* - A conversion is only done if the path len is larger than 245. Otherwise
|
||||||
|
* the windows API functions work with the normal "unixoid" representation too.
|
||||||
|
*/
|
||||||
|
template<typename S>
|
||||||
|
S pathtoUNC(const S &str)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
S longStr;
|
||||||
|
|
||||||
|
len = str.length();
|
||||||
|
longStr.reserve(len+4);
|
||||||
|
|
||||||
|
// prepend \\?\ and convert '/' => '\' to support long names
|
||||||
|
if( str[0] == '/' || str[0] == '\\' ) {
|
||||||
|
// Don't prepend if already UNC
|
||||||
|
if( !(len > 1 && (str[1] == '/' || str[1] == '\\')) ) {
|
||||||
|
longStr.append("\\\\?");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
longStr.append("\\\\?\\"); // prepend string by this four magic chars.
|
||||||
|
}
|
||||||
|
longStr += str;
|
||||||
|
|
||||||
|
/* replace all occurences of / with the windows native \ */
|
||||||
|
|
||||||
|
for (auto it = longStr.begin(); it != longStr.end(); ++it) {
|
||||||
|
if(*it == '/') {
|
||||||
|
*it = '\\';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return longStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
}
|
|
@ -128,6 +128,11 @@ generate_export_header( ${CSYNC_LIBRARY}
|
||||||
target_link_libraries(${CSYNC_LIBRARY} ${CSYNC_LINK_LIBRARIES})
|
target_link_libraries(${CSYNC_LIBRARY} ${CSYNC_LINK_LIBRARIES})
|
||||||
#target_link_libraries(${CSYNC_LIBRARY}_static ${CSYNC_LINK_LIBRARIES})
|
#target_link_libraries(${CSYNC_LIBRARY}_static ${CSYNC_LINK_LIBRARIES})
|
||||||
|
|
||||||
|
if(ZLIB_FOUND)
|
||||||
|
target_link_libraries(${CSYNC_LIBRARY} ${ZLIB_LIBRARIES})
|
||||||
|
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||||
|
endif(ZLIB_FOUND)
|
||||||
|
|
||||||
find_package(Qt5Core REQUIRED)
|
find_package(Qt5Core REQUIRED)
|
||||||
qt5_use_modules(${CSYNC_LIBRARY} Core)
|
qt5_use_modules(${CSYNC_LIBRARY} Core)
|
||||||
|
|
||||||
|
|
|
@ -390,63 +390,3 @@ int c_parse_uri(const char *uri,
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This function takes a path and converts it to a UNC representation of the
|
|
||||||
* string. That means that it prepends a \\?\ (unless already UNC) and converts
|
|
||||||
* all slashes to backslashes.
|
|
||||||
*
|
|
||||||
* Note the following:
|
|
||||||
* - The string must be absolute.
|
|
||||||
* - it needs to contain a drive character to be a valid UNC
|
|
||||||
* - A conversion is only done if the path len is larger than 245. Otherwise
|
|
||||||
* the windows API functions work with the normal "unixoid" representation too.
|
|
||||||
*
|
|
||||||
* This function allocates memory that must be freed by the caller.
|
|
||||||
*/
|
|
||||||
const char *c_path_to_UNC(const char *str)
|
|
||||||
{
|
|
||||||
int len = 0;
|
|
||||||
char *longStr = NULL;
|
|
||||||
|
|
||||||
len = strlen(str);
|
|
||||||
longStr = c_malloc(len+5);
|
|
||||||
*longStr = '\0';
|
|
||||||
|
|
||||||
// prepend \\?\ and convert '/' => '\' to support long names
|
|
||||||
if( str[0] == '/' || str[0] == '\\' ) {
|
|
||||||
// Don't prepend if already UNC
|
|
||||||
if( !(len > 1 && (str[1] == '/' || str[1] == '\\')) ) {
|
|
||||||
strcpy( longStr, "\\\\?");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
strcpy( longStr, "\\\\?\\"); // prepend string by this four magic chars.
|
|
||||||
}
|
|
||||||
strncat( longStr, str, len );
|
|
||||||
|
|
||||||
/* replace all occurences of / with the windows native \ */
|
|
||||||
char *c = longStr;
|
|
||||||
for (; *c; ++c) {
|
|
||||||
if(*c == '/') {
|
|
||||||
*c = '\\';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return longStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
mbchar_t* c_utf8_path_to_locale(const char *str)
|
|
||||||
{
|
|
||||||
if( str == NULL ) {
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
#ifdef _WIN32
|
|
||||||
const char *unc_str = c_path_to_UNC(str);
|
|
||||||
mbchar_t *dst = c_utf8_string_to_locale(unc_str);
|
|
||||||
SAFE_FREE(unc_str);
|
|
||||||
return dst;
|
|
||||||
#else
|
|
||||||
return c_utf8_string_to_locale(str);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -112,34 +112,6 @@ typedef struct
|
||||||
char * extension;
|
char * extension;
|
||||||
} C_PATHINFO;
|
} C_PATHINFO;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief c_path_to_UNC converts a unixoid path to UNC format.
|
|
||||||
*
|
|
||||||
* It converts the '/' to '\' and prepends \\?\ to the path.
|
|
||||||
*
|
|
||||||
* A proper windows path has to have a drive letter, otherwise it is not
|
|
||||||
* valid UNC.
|
|
||||||
*
|
|
||||||
* @param str The path to convert
|
|
||||||
*
|
|
||||||
* @return a pointer to the converted string. Caller has to free it.
|
|
||||||
*/
|
|
||||||
const char *c_path_to_UNC(const char *str);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief c_utf8_path_to_locale converts a unixoid path to the locale aware format
|
|
||||||
*
|
|
||||||
* On windows, it converts to UNC and multibyte.
|
|
||||||
* On Mac, it converts to the correct utf8 using iconv.
|
|
||||||
* On Linux, it returns utf8
|
|
||||||
*
|
|
||||||
* @param str The path to convert
|
|
||||||
*
|
|
||||||
* @return a pointer to the converted string. Caller has to free it using the
|
|
||||||
* function c_free_locale_string.
|
|
||||||
*/
|
|
||||||
mbchar_t* c_utf8_path_to_locale(const char *str);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* }@
|
* }@
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
|
|
||||||
#include "c_alloc.h"
|
#include "c_alloc.h"
|
||||||
#include "c_string.h"
|
#include "c_string.h"
|
||||||
|
#include "common/filesystembase.h"
|
||||||
|
|
||||||
/* Convert a locale String to UTF8 */
|
/* Convert a locale String to UTF8 */
|
||||||
QByteArray c_utf8_from_locale(const mbchar_t *wstr)
|
QByteArray c_utf8_from_locale(const mbchar_t *wstr)
|
||||||
|
@ -99,4 +100,19 @@ mbchar_t* c_utf8_string_to_locale(const char *str)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mbchar_t* c_utf8_path_to_locale(const char *str)
|
||||||
|
{
|
||||||
|
if( str == NULL ) {
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
#ifdef _WIN32
|
||||||
|
QByteArray unc_str = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(str, strlen(str)));
|
||||||
|
mbchar_t *dst = c_utf8_string_to_locale(unc_str);
|
||||||
|
return dst;
|
||||||
|
#else
|
||||||
|
return c_utf8_string_to_locale(str);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,6 +91,20 @@ extern "C" {
|
||||||
*/
|
*/
|
||||||
mbchar_t* c_utf8_string_to_locale(const char *wstr);
|
mbchar_t* c_utf8_string_to_locale(const char *wstr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief c_utf8_path_to_locale converts a unixoid path to the locale aware format
|
||||||
|
*
|
||||||
|
* On windows, it converts to UNC and multibyte.
|
||||||
|
* On Mac, it converts to the correct utf8 using iconv.
|
||||||
|
* On Linux, it returns utf8
|
||||||
|
*
|
||||||
|
* @param str The path to convert
|
||||||
|
*
|
||||||
|
* @return a pointer to the converted string. Caller has to free it using the
|
||||||
|
* function c_free_locale_string.
|
||||||
|
*/
|
||||||
|
mbchar_t* c_utf8_path_to_locale(const char *str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Free buffer malloced by c_utf8_to_locale().
|
* @brief Free buffer malloced by c_utf8_to_locale().
|
||||||
*
|
*
|
||||||
|
|
|
@ -115,11 +115,6 @@ if(INOTIFY_FOUND)
|
||||||
link_directories(${INOTIFY_LIBRARY_DIR})
|
link_directories(${INOTIFY_LIBRARY_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ZLIB_FOUND)
|
|
||||||
list(APPEND libsync_LINK_TARGETS ${ZLIB_LIBRARIES})
|
|
||||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
|
||||||
endif(ZLIB_FOUND)
|
|
||||||
|
|
||||||
add_library(${synclib_NAME} SHARED ${libsync_SRCS} ${syncMoc})
|
add_library(${synclib_NAME} SHARED ${libsync_SRCS} ${syncMoc})
|
||||||
GENERATE_EXPORT_HEADER( ${synclib_NAME}
|
GENERATE_EXPORT_HEADER( ${synclib_NAME}
|
||||||
BASE_NAME ${synclib_NAME}
|
BASE_NAME ${synclib_NAME}
|
||||||
|
|
|
@ -17,21 +17,6 @@
|
||||||
#include "common/utility.h"
|
#include "common/utility.h"
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QLoggingCategory>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QCryptographicHash>
|
|
||||||
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
#include <zlib.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include <windows.h>
|
|
||||||
#include <windef.h>
|
|
||||||
#include <winbase.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <io.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We use some internals of csync:
|
// We use some internals of csync:
|
||||||
extern "C" int c_utimes(const char *, const struct timeval *);
|
extern "C" int c_utimes(const char *, const struct timeval *);
|
||||||
|
@ -44,20 +29,6 @@ extern "C" int c_utimes(const char *, const struct timeval *);
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(lcFileSystem, "sync.filesystem", QtInfoMsg)
|
|
||||||
|
|
||||||
QString FileSystem::longWinPath(const QString &inpath)
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
const char *unc_str = c_path_to_UNC(inpath.toUtf8());
|
|
||||||
QString path = QString::fromUtf8(unc_str);
|
|
||||||
free((void*)unc_str);
|
|
||||||
return path;
|
|
||||||
#else
|
|
||||||
return inpath;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileSystem::fileEquals(const QString &fn1, const QString &fn2)
|
bool FileSystem::fileEquals(const QString &fn1, const QString &fn2)
|
||||||
{
|
{
|
||||||
// compare two files with given filename and return true if they have the same content
|
// compare two files with given filename and return true if they have the same content
|
||||||
|
@ -91,83 +62,6 @@ bool FileSystem::fileEquals(const QString &fn1, const QString &fn2)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystem::setFileHidden(const QString &filename, bool hidden)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
QString fName = longWinPath(filename);
|
|
||||||
DWORD dwAttrs;
|
|
||||||
|
|
||||||
dwAttrs = GetFileAttributesW((wchar_t *)fName.utf16());
|
|
||||||
|
|
||||||
if (dwAttrs != INVALID_FILE_ATTRIBUTES) {
|
|
||||||
if (hidden && !(dwAttrs & FILE_ATTRIBUTE_HIDDEN)) {
|
|
||||||
SetFileAttributesW((wchar_t *)fName.utf16(), dwAttrs | FILE_ATTRIBUTE_HIDDEN);
|
|
||||||
} else if (!hidden && (dwAttrs & FILE_ATTRIBUTE_HIDDEN)) {
|
|
||||||
SetFileAttributesW((wchar_t *)fName.utf16(), dwAttrs & ~FILE_ATTRIBUTE_HIDDEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
Q_UNUSED(filename);
|
|
||||||
Q_UNUSED(hidden);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static QFile::Permissions getDefaultWritePermissions()
|
|
||||||
{
|
|
||||||
QFile::Permissions result = QFile::WriteUser;
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
mode_t mask = umask(0);
|
|
||||||
umask(mask);
|
|
||||||
if (!(mask & S_IWGRP)) {
|
|
||||||
result |= QFile::WriteGroup;
|
|
||||||
}
|
|
||||||
if (!(mask & S_IWOTH)) {
|
|
||||||
result |= QFile::WriteOther;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSystem::setFileReadOnly(const QString &filename, bool readonly)
|
|
||||||
{
|
|
||||||
QFile file(filename);
|
|
||||||
QFile::Permissions permissions = file.permissions();
|
|
||||||
|
|
||||||
QFile::Permissions allWritePermissions =
|
|
||||||
QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther | QFile::WriteOwner;
|
|
||||||
static QFile::Permissions defaultWritePermissions = getDefaultWritePermissions();
|
|
||||||
|
|
||||||
permissions &= ~allWritePermissions;
|
|
||||||
if (!readonly) {
|
|
||||||
permissions |= defaultWritePermissions;
|
|
||||||
}
|
|
||||||
file.setPermissions(permissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSystem::setFolderMinimumPermissions(const QString &filename)
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
QFile::Permissions perm = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
|
|
||||||
QFile file(filename);
|
|
||||||
file.setPermissions(perm);
|
|
||||||
#else
|
|
||||||
Q_UNUSED(filename);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void FileSystem::setFileReadOnlyWeak(const QString &filename, bool readonly)
|
|
||||||
{
|
|
||||||
QFile file(filename);
|
|
||||||
QFile::Permissions permissions = file.permissions();
|
|
||||||
|
|
||||||
if (!readonly && (permissions & QFile::WriteOwner)) {
|
|
||||||
return; // already writable enough
|
|
||||||
}
|
|
||||||
|
|
||||||
setFileReadOnly(filename, readonly);
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t FileSystem::getModTime(const QString &filename)
|
time_t FileSystem::getModTime(const QString &filename)
|
||||||
{
|
{
|
||||||
csync_file_stat_t stat;
|
csync_file_stat_t stat;
|
||||||
|
@ -197,57 +91,6 @@ bool FileSystem::setModTime(const QString &filename, time_t modTime)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
static bool isLnkFile(const QString &filename)
|
|
||||||
{
|
|
||||||
return filename.endsWith(".lnk");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool FileSystem::rename(const QString &originFileName,
|
|
||||||
const QString &destinationFileName,
|
|
||||||
QString *errorString)
|
|
||||||
{
|
|
||||||
bool success = false;
|
|
||||||
QString error;
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
QString orig = longWinPath(originFileName);
|
|
||||||
QString dest = longWinPath(destinationFileName);
|
|
||||||
|
|
||||||
if (isLnkFile(originFileName) || isLnkFile(destinationFileName)) {
|
|
||||||
success = MoveFileEx((wchar_t *)orig.utf16(),
|
|
||||||
(wchar_t *)dest.utf16(),
|
|
||||||
MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH);
|
|
||||||
if (!success) {
|
|
||||||
wchar_t *string = 0;
|
|
||||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
||||||
NULL, ::GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
||||||
(LPWSTR)&string, 0, NULL);
|
|
||||||
|
|
||||||
error = QString::fromWCharArray(string);
|
|
||||||
LocalFree((HLOCAL)string);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
QFile orig(originFileName);
|
|
||||||
success = orig.rename(destinationFileName);
|
|
||||||
if (!success) {
|
|
||||||
error = orig.errorString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
qCWarning(lcFileSystem) << "Error renaming file" << originFileName
|
|
||||||
<< "to" << destinationFileName
|
|
||||||
<< "failed: " << error;
|
|
||||||
if (errorString) {
|
|
||||||
*errorString = error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileSystem::fileChanged(const QString &fileName,
|
bool FileSystem::fileChanged(const QString &fileName,
|
||||||
qint64 previousSize,
|
qint64 previousSize,
|
||||||
time_t previousMtime)
|
time_t previousMtime)
|
||||||
|
@ -271,151 +114,6 @@ bool FileSystem::verifyFileUnchanged(const QString &fileName,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystem::renameReplace(const QString &originFileName,
|
|
||||||
const QString &destinationFileName,
|
|
||||||
qint64 destinationSize,
|
|
||||||
time_t destinationMtime,
|
|
||||||
QString *errorString)
|
|
||||||
{
|
|
||||||
if (fileExists(destinationFileName)
|
|
||||||
&& fileChanged(destinationFileName, destinationSize, destinationMtime)) {
|
|
||||||
if (errorString) {
|
|
||||||
*errorString = qApp->translate("FileSystem",
|
|
||||||
"The destination file has an unexpected size or modification time");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return uncheckedRenameReplace(originFileName, destinationFileName, errorString);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileSystem::uncheckedRenameReplace(const QString &originFileName,
|
|
||||||
const QString &destinationFileName,
|
|
||||||
QString *errorString)
|
|
||||||
{
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
bool success;
|
|
||||||
QFile orig(originFileName);
|
|
||||||
// We want a rename that also overwites. QFile::rename does not overwite.
|
|
||||||
// Qt 5.1 has QSaveFile::renameOverwrite we could use.
|
|
||||||
// ### FIXME
|
|
||||||
success = true;
|
|
||||||
bool destExists = fileExists(destinationFileName);
|
|
||||||
if (destExists && !QFile::remove(destinationFileName)) {
|
|
||||||
*errorString = orig.errorString();
|
|
||||||
qCWarning(lcFileSystem) << "Target file could not be removed.";
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
if (success) {
|
|
||||||
success = orig.rename(destinationFileName);
|
|
||||||
}
|
|
||||||
if (!success) {
|
|
||||||
*errorString = orig.errorString();
|
|
||||||
qCWarning(lcFileSystem) << "Renaming temp file to final failed: " << *errorString;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else //Q_OS_WIN
|
|
||||||
// You can not overwrite a read-only file on windows.
|
|
||||||
if (!QFileInfo(destinationFileName).isWritable()) {
|
|
||||||
setFileReadOnly(destinationFileName, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL ok;
|
|
||||||
QString orig = longWinPath(originFileName);
|
|
||||||
QString dest = longWinPath(destinationFileName);
|
|
||||||
|
|
||||||
ok = MoveFileEx((wchar_t *)orig.utf16(),
|
|
||||||
(wchar_t *)dest.utf16(),
|
|
||||||
MOVEFILE_REPLACE_EXISTING + MOVEFILE_COPY_ALLOWED + MOVEFILE_WRITE_THROUGH);
|
|
||||||
if (!ok) {
|
|
||||||
wchar_t *string = 0;
|
|
||||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
||||||
NULL, ::GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
||||||
(LPWSTR)&string, 0, NULL);
|
|
||||||
|
|
||||||
*errorString = QString::fromWCharArray(string);
|
|
||||||
qCWarning(lcFileSystem) << "Renaming temp file to final failed: " << *errorString;
|
|
||||||
LocalFree((HLOCAL)string);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileSystem::openAndSeekFileSharedRead(QFile *file, QString *errorOrNull, qint64 seek)
|
|
||||||
{
|
|
||||||
QString errorDummy;
|
|
||||||
// avoid many if (errorOrNull) later.
|
|
||||||
QString &error = errorOrNull ? *errorOrNull : errorDummy;
|
|
||||||
error.clear();
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
//
|
|
||||||
// The following code is adapted from Qt's QFSFileEnginePrivate::nativeOpen()
|
|
||||||
// by including the FILE_SHARE_DELETE share mode.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Enable full sharing.
|
|
||||||
DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
|
||||||
|
|
||||||
int accessRights = GENERIC_READ;
|
|
||||||
DWORD creationDisp = OPEN_EXISTING;
|
|
||||||
|
|
||||||
// Create the file handle.
|
|
||||||
SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
|
|
||||||
QString fName = longWinPath(file->fileName());
|
|
||||||
|
|
||||||
HANDLE fileHandle = CreateFileW(
|
|
||||||
(const wchar_t *)fName.utf16(),
|
|
||||||
accessRights,
|
|
||||||
shareMode,
|
|
||||||
&securityAtts,
|
|
||||||
creationDisp,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
// Bail out on error.
|
|
||||||
if (fileHandle == INVALID_HANDLE_VALUE) {
|
|
||||||
error = qt_error_string();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the HANDLE to an fd and pass it to QFile's foreign-open
|
|
||||||
// function. The fd owns the handle, so when QFile later closes
|
|
||||||
// the fd the handle will be closed too.
|
|
||||||
int fd = _open_osfhandle((intptr_t)fileHandle, _O_RDONLY);
|
|
||||||
if (fd == -1) {
|
|
||||||
error = "could not make fd from handle";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!file->open(fd, QIODevice::ReadOnly, QFile::AutoCloseHandle)) {
|
|
||||||
error = file->errorString();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seek to the right spot
|
|
||||||
LARGE_INTEGER *li = reinterpret_cast<LARGE_INTEGER *>(&seek);
|
|
||||||
DWORD newFilePointer = SetFilePointer(fileHandle, li->LowPart, &li->HighPart, FILE_BEGIN);
|
|
||||||
if (newFilePointer == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
|
|
||||||
error = qt_error_string();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
if (!file->open(QFile::ReadOnly)) {
|
|
||||||
error = file->errorString();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!file->seek(seek)) {
|
|
||||||
error = file->errorString();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
static qint64 getSizeWithCsync(const QString &filename)
|
static qint64 getSizeWithCsync(const QString &filename)
|
||||||
{
|
{
|
||||||
|
@ -441,190 +139,5 @@ qint64 FileSystem::getSize(const QString &filename)
|
||||||
return QFileInfo(filename).size();
|
return QFileInfo(filename).size();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
static bool fileExistsWin(const QString &filename)
|
|
||||||
{
|
|
||||||
WIN32_FIND_DATA FindFileData;
|
|
||||||
HANDLE hFind;
|
|
||||||
QString fName = FileSystem::longWinPath(filename);
|
|
||||||
|
|
||||||
hFind = FindFirstFileW((wchar_t *)fName.utf16(), &FindFileData);
|
|
||||||
if (hFind == INVALID_HANDLE_VALUE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
FindClose(hFind);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool FileSystem::fileExists(const QString &filename, const QFileInfo &fileInfo)
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
if (isLnkFile(filename)) {
|
|
||||||
// Use a native check.
|
|
||||||
return fileExistsWin(filename);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
bool re = fileInfo.exists();
|
|
||||||
// if the filename is different from the filename in fileInfo, the fileInfo is
|
|
||||||
// not valid. There needs to be one initialised here. Otherwise the incoming
|
|
||||||
// fileInfo is re-used.
|
|
||||||
if (fileInfo.filePath() != filename) {
|
|
||||||
QFileInfo myFI(filename);
|
|
||||||
re = myFI.exists();
|
|
||||||
}
|
|
||||||
return re;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
QString FileSystem::fileSystemForPath(const QString &path)
|
|
||||||
{
|
|
||||||
// See also QStorageInfo (Qt >=5.4) and GetVolumeInformationByHandleW (>= Vista)
|
|
||||||
QString drive = path.left(2);
|
|
||||||
if (!drive.endsWith(":"))
|
|
||||||
return QString();
|
|
||||||
drive.append('\\');
|
|
||||||
|
|
||||||
const size_t fileSystemBufferSize = 4096;
|
|
||||||
TCHAR fileSystemBuffer[fileSystemBufferSize];
|
|
||||||
|
|
||||||
if (!GetVolumeInformationW(
|
|
||||||
reinterpret_cast<LPCWSTR>(drive.utf16()),
|
|
||||||
NULL, 0,
|
|
||||||
NULL, NULL, NULL,
|
|
||||||
fileSystemBuffer, fileSystemBufferSize)) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return QString::fromUtf16(reinterpret_cast<const ushort *>(fileSystemBuffer));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define BUFSIZE qint64(500 * 1024) // 500 KiB
|
|
||||||
|
|
||||||
static QByteArray readToCrypto(const QString &filename, QCryptographicHash::Algorithm algo)
|
|
||||||
{
|
|
||||||
QFile file(filename);
|
|
||||||
const qint64 bufSize = qMin(BUFSIZE, file.size() + 1);
|
|
||||||
QByteArray buf(bufSize, Qt::Uninitialized);
|
|
||||||
QByteArray arr;
|
|
||||||
QCryptographicHash crypto(algo);
|
|
||||||
|
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
|
||||||
qint64 size;
|
|
||||||
while (!file.atEnd()) {
|
|
||||||
size = file.read(buf.data(), bufSize);
|
|
||||||
if (size > 0) {
|
|
||||||
crypto.addData(buf.data(), size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arr = crypto.result().toHex();
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray FileSystem::calcMd5(const QString &filename)
|
|
||||||
{
|
|
||||||
return readToCrypto(filename, QCryptographicHash::Md5);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray FileSystem::calcSha1(const QString &filename)
|
|
||||||
{
|
|
||||||
return readToCrypto(filename, QCryptographicHash::Sha1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
QByteArray FileSystem::calcAdler32(const QString &filename)
|
|
||||||
{
|
|
||||||
QFile file(filename);
|
|
||||||
const qint64 bufSize = qMin(BUFSIZE, file.size() + 1);
|
|
||||||
QByteArray buf(bufSize, Qt::Uninitialized);
|
|
||||||
|
|
||||||
unsigned int adler = adler32(0L, Z_NULL, 0);
|
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
|
||||||
qint64 size;
|
|
||||||
while (!file.atEnd()) {
|
|
||||||
size = file.read(buf.data(), bufSize);
|
|
||||||
if (size > 0)
|
|
||||||
adler = adler32(adler, (const Bytef *)buf.data(), size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return QByteArray::number(adler, 16);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QString FileSystem::makeConflictFileName(const QString &fn, const QDateTime &dt)
|
|
||||||
{
|
|
||||||
QString conflictFileName(fn);
|
|
||||||
// Add _conflict-XXXX before the extension.
|
|
||||||
int dotLocation = conflictFileName.lastIndexOf('.');
|
|
||||||
// If no extension, add it at the end (take care of cases like foo/.hidden or foo.bar/file)
|
|
||||||
if (dotLocation <= conflictFileName.lastIndexOf('/') + 1) {
|
|
||||||
dotLocation = conflictFileName.size();
|
|
||||||
}
|
|
||||||
QString timeString = dt.toString("yyyyMMdd-hhmmss");
|
|
||||||
|
|
||||||
// Additional marker
|
|
||||||
QByteArray conflictFileUserName = qgetenv("CSYNC_CONFLICT_FILE_USERNAME");
|
|
||||||
if (conflictFileUserName.isEmpty())
|
|
||||||
conflictFileName.insert(dotLocation, "_conflict-" + timeString);
|
|
||||||
else
|
|
||||||
conflictFileName.insert(dotLocation, "_conflict_" + QString::fromUtf8(conflictFileUserName) + "-" + timeString);
|
|
||||||
|
|
||||||
return conflictFileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileSystem::remove(const QString &fileName, QString *errorString)
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
// You cannot delete a read-only file on windows, but we want to
|
|
||||||
// allow that.
|
|
||||||
if (!QFileInfo(fileName).isWritable()) {
|
|
||||||
setFileReadOnly(fileName, false);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
QFile f(fileName);
|
|
||||||
if (!f.remove()) {
|
|
||||||
if (errorString) {
|
|
||||||
*errorString = f.errorString();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileSystem::isFileLocked(const QString &fileName)
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
mbchar_t *wuri = c_utf8_path_to_locale(fileName.toUtf8());
|
|
||||||
|
|
||||||
// Check if file exists
|
|
||||||
DWORD attr = GetFileAttributesW(wuri);
|
|
||||||
if (attr != INVALID_FILE_ATTRIBUTES) {
|
|
||||||
// Try to open the file with as much access as possible..
|
|
||||||
HANDLE win_h = CreateFileW(
|
|
||||||
wuri,
|
|
||||||
GENERIC_READ | GENERIC_WRITE,
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
||||||
NULL, OPEN_EXISTING,
|
|
||||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
c_free_locale_string(wuri);
|
|
||||||
if (win_h == INVALID_HANDLE_VALUE) {
|
|
||||||
/* could not be opened, so locked? */
|
|
||||||
/* 32 == ERROR_SHARING_VIOLATION */
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
CloseHandle(win_h);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c_free_locale_string(wuri);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
Q_UNUSED(fileName);
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|
|
@ -18,13 +18,12 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <QCryptographicHash>
|
|
||||||
#include <QFileInfo>
|
|
||||||
|
|
||||||
#include <owncloudlib.h>
|
#include <owncloudlib.h>
|
||||||
|
// Chain in the base include and extend the namespace
|
||||||
|
#include "common/filesystembase.h"
|
||||||
|
|
||||||
class QFile;
|
class QFile;
|
||||||
class QFileInfo;
|
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
@ -43,39 +42,6 @@ namespace FileSystem {
|
||||||
*/
|
*/
|
||||||
bool fileEquals(const QString &fn1, const QString &fn2);
|
bool fileEquals(const QString &fn1, const QString &fn2);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Mark the file as hidden (only has effects on windows)
|
|
||||||
*/
|
|
||||||
void OWNCLOUDSYNC_EXPORT setFileHidden(const QString &filename, bool hidden);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Marks the file as read-only.
|
|
||||||
*
|
|
||||||
* On linux this either revokes all 'w' permissions or restores permissions
|
|
||||||
* according to the umask.
|
|
||||||
*/
|
|
||||||
void OWNCLOUDSYNC_EXPORT setFileReadOnly(const QString &filename, bool readonly);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Marks the file as read-only.
|
|
||||||
*
|
|
||||||
* It's like setFileReadOnly(), but weaker: if readonly is false and the user
|
|
||||||
* already has write permissions, no change to the permissions is made.
|
|
||||||
*
|
|
||||||
* This means that it will preserve explicitly set rw-r--r-- permissions even
|
|
||||||
* when the umask is 0002. (setFileReadOnly() would adjust to rw-rw-r--)
|
|
||||||
*/
|
|
||||||
void OWNCLOUDSYNC_EXPORT setFileReadOnlyWeak(const QString &filename, bool readonly);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Try to set permissions so that other users on the local machine can not
|
|
||||||
* go into the folder.
|
|
||||||
*/
|
|
||||||
void OWNCLOUDSYNC_EXPORT setFolderMinimumPermissions(const QString &filename);
|
|
||||||
|
|
||||||
/** convert a "normal" windows path into a path that can be 32k chars long. */
|
|
||||||
QString OWNCLOUDSYNC_EXPORT longWinPath(const QString &inpath);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the mtime for a filepath
|
* @brief Get the mtime for a filepath
|
||||||
*
|
*
|
||||||
|
@ -94,23 +60,6 @@ namespace FileSystem {
|
||||||
*/
|
*/
|
||||||
qint64 OWNCLOUDSYNC_EXPORT getSize(const QString &filename);
|
qint64 OWNCLOUDSYNC_EXPORT getSize(const QString &filename);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks whether a file exists.
|
|
||||||
*
|
|
||||||
* Use this over QFileInfo::exists() and QFile::exists() to avoid bugs with lnk
|
|
||||||
* files, see above.
|
|
||||||
*/
|
|
||||||
bool OWNCLOUDSYNC_EXPORT fileExists(const QString &filename, const QFileInfo & = QFileInfo());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Rename the file \a originFileName to \a destinationFileName.
|
|
||||||
*
|
|
||||||
* It behaves as QFile::rename() but handles .lnk files correctly on Windows.
|
|
||||||
*/
|
|
||||||
bool OWNCLOUDSYNC_EXPORT rename(const QString &originFileName,
|
|
||||||
const QString &destinationFileName,
|
|
||||||
QString *errorString = NULL);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check if \a fileName has changed given previous size and mtime
|
* @brief Check if \a fileName has changed given previous size and mtime
|
||||||
*
|
*
|
||||||
|
@ -128,71 +77,6 @@ namespace FileSystem {
|
||||||
bool verifyFileUnchanged(const QString &fileName,
|
bool verifyFileUnchanged(const QString &fileName,
|
||||||
qint64 previousSize,
|
qint64 previousSize,
|
||||||
time_t previousMtime);
|
time_t previousMtime);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief renames a file, overriding the target if it exists
|
|
||||||
*
|
|
||||||
* Rename the file \a originFileName to \a destinationFileName, and
|
|
||||||
* overwrite the destination if it already exists - as long as the
|
|
||||||
* destination file has the expected \a destinationSize and
|
|
||||||
* \a destinationMtime.
|
|
||||||
*
|
|
||||||
* If the destination file does not exist, the given size and mtime are
|
|
||||||
* ignored.
|
|
||||||
*/
|
|
||||||
bool renameReplace(const QString &originFileName,
|
|
||||||
const QString &destinationFileName,
|
|
||||||
qint64 destinationSize,
|
|
||||||
time_t destinationMtime,
|
|
||||||
QString *errorString);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rename the file \a originFileName to \a destinationFileName, and
|
|
||||||
* overwrite the destination if it already exists - without extra checks.
|
|
||||||
*/
|
|
||||||
bool uncheckedRenameReplace(const QString &originFileName,
|
|
||||||
const QString &destinationFileName,
|
|
||||||
QString *errorString);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a file.
|
|
||||||
*
|
|
||||||
* Equivalent to QFile::remove(), except on Windows, where it will also
|
|
||||||
* successfully remove read-only files.
|
|
||||||
*/
|
|
||||||
bool OWNCLOUDSYNC_EXPORT remove(const QString &fileName, QString *errorString = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replacement for QFile::open(ReadOnly) followed by a seek().
|
|
||||||
* This version sets a more permissive sharing mode on Windows.
|
|
||||||
*
|
|
||||||
* Warning: The resulting file may have an empty fileName and be unsuitable for use
|
|
||||||
* with QFileInfo! Calling seek() on the QFile with >32bit signed values will fail!
|
|
||||||
*/
|
|
||||||
bool openAndSeekFileSharedRead(QFile *file, QString *error, qint64 seek);
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
/**
|
|
||||||
* Returns the file system used at the given path.
|
|
||||||
*/
|
|
||||||
QString fileSystemForPath(const QString &path);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QByteArray OWNCLOUDSYNC_EXPORT calcMd5(const QString &fileName);
|
|
||||||
QByteArray OWNCLOUDSYNC_EXPORT calcSha1(const QString &fileName);
|
|
||||||
#ifdef ZLIB_FOUND
|
|
||||||
QByteArray OWNCLOUDSYNC_EXPORT calcAdler32(const QString &fileName);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a file name based on \a fn that's suitable for a conflict.
|
|
||||||
*/
|
|
||||||
QString OWNCLOUDSYNC_EXPORT makeConflictFileName(const QString &fn, const QDateTime &dt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true when a file is locked. (Windows only)
|
|
||||||
*/
|
|
||||||
bool OWNCLOUDSYNC_EXPORT isFileLocked(const QString &fileName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include "c_string.h"
|
#include "c_string.h"
|
||||||
#include "c_path.h"
|
#include "c_path.h"
|
||||||
#include "c_utf8.h"
|
#include "c_utf8.h"
|
||||||
|
#include "common/filesystembase.h"
|
||||||
|
#include "torture.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -116,41 +118,36 @@ static void check_long_win_path(void **state)
|
||||||
{
|
{
|
||||||
const char *path = "C://DATA/FILES/MUSIC/MY_MUSIC.mp3"; // check a short path
|
const char *path = "C://DATA/FILES/MUSIC/MY_MUSIC.mp3"; // check a short path
|
||||||
const char *exp_path = "\\\\?\\C:\\\\DATA\\FILES\\MUSIC\\MY_MUSIC.mp3";
|
const char *exp_path = "\\\\?\\C:\\\\DATA\\FILES\\MUSIC\\MY_MUSIC.mp3";
|
||||||
const char *new_short = c_path_to_UNC(path);
|
QByteArray new_short = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(path, strlen(path)));
|
||||||
assert_string_equal(new_short, exp_path);
|
assert_string_equal(new_short, exp_path);
|
||||||
SAFE_FREE(new_short);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const char *path = "\\\\foo\\bar/MY_MUSIC.mp3";
|
const char *path = "\\\\foo\\bar/MY_MUSIC.mp3";
|
||||||
const char *exp_path = "\\\\foo\\bar\\MY_MUSIC.mp3";
|
const char *exp_path = "\\\\foo\\bar\\MY_MUSIC.mp3";
|
||||||
const char *new_short = c_path_to_UNC(path);
|
QByteArray new_short = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(path, strlen(path)));
|
||||||
assert_string_equal(new_short, exp_path);
|
assert_string_equal(new_short, exp_path);
|
||||||
SAFE_FREE(new_short);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const char *path = "//foo\\bar/MY_MUSIC.mp3";
|
const char *path = "//foo\\bar/MY_MUSIC.mp3";
|
||||||
const char *exp_path = "\\\\foo\\bar\\MY_MUSIC.mp3";
|
const char *exp_path = "\\\\foo\\bar\\MY_MUSIC.mp3";
|
||||||
const char *new_short = c_path_to_UNC(path);
|
QByteArray new_short = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(path, strlen(path)));
|
||||||
assert_string_equal(new_short, exp_path);
|
assert_string_equal(new_short, exp_path);
|
||||||
SAFE_FREE(new_short);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const char *path = "\\foo\\bar";
|
const char *path = "\\foo\\bar";
|
||||||
const char *exp_path = "\\\\?\\foo\\bar";
|
const char *exp_path = "\\\\?\\foo\\bar";
|
||||||
const char *new_short = c_path_to_UNC(path);
|
QByteArray new_short = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(path, strlen(path)));
|
||||||
assert_string_equal(new_short, exp_path);
|
assert_string_equal(new_short, exp_path);
|
||||||
SAFE_FREE(new_short);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const char *path = "/foo/bar";
|
const char *path = "/foo/bar";
|
||||||
const char *exp_path = "\\\\?\\foo\\bar";
|
const char *exp_path = "\\\\?\\foo\\bar";
|
||||||
const char *new_short = c_path_to_UNC(path);
|
QByteArray new_short = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(path, strlen(path)));
|
||||||
assert_string_equal(new_short, exp_path);
|
assert_string_equal(new_short, exp_path);
|
||||||
SAFE_FREE(new_short);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *longPath = "D://alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/"
|
const char *longPath = "D://alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/"
|
||||||
|
@ -162,14 +159,13 @@ static void check_long_win_path(void **state)
|
||||||
"jlonglonglonglong\\klonglonglonglong\\llonglonglonglong\\mlonglonglonglong\\nlonglonglonglong\\"
|
"jlonglonglonglong\\klonglonglonglong\\llonglonglonglong\\mlonglonglonglong\\nlonglonglonglong\\"
|
||||||
"olonglonglonglong\\file.txt";
|
"olonglonglonglong\\file.txt";
|
||||||
|
|
||||||
const char *new_long = c_path_to_UNC(longPath);
|
QByteArray new_long = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(longPath, strlen(longPath)));
|
||||||
// printf( "XXXXXXXXXXXX %s %d\n", new_long, mem_reserved);
|
// printf( "XXXXXXXXXXXX %s %d\n", new_long, mem_reserved);
|
||||||
|
|
||||||
assert_string_equal(new_long, longPathConv);
|
assert_string_equal(new_long, longPathConv);
|
||||||
|
|
||||||
// printf( "YYYYYYYYYYYY %ld\n", strlen(new_long));
|
// printf( "YYYYYYYYYYYY %ld\n", strlen(new_long));
|
||||||
assert_int_equal( strlen(new_long), 286);
|
assert_int_equal( strlen(new_long), 286);
|
||||||
SAFE_FREE(new_long);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int torture_run_tests(void)
|
int torture_run_tests(void)
|
||||||
|
|
Loading…
Reference in a new issue