/* * Copyright (C) by Daniel Molkentin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License * for more details. */ #include "filesystem.h" #include "common/utility.h" #include #include #include #include #include // We use some internals of csync: extern "C" int c_utimes(const char *, const struct timeval *); #include "csync.h" #include "vio/csync_vio_local.h" #include "std/c_string.h" #include "std/c_utf8.h" namespace OCC { bool FileSystem::fileEquals(const QString &fn1, const QString &fn2) { // compare two files with given filename and return true if they have the same content QFile f1(fn1); QFile f2(fn2); if (!f1.open(QIODevice::ReadOnly) || !f2.open(QIODevice::ReadOnly)) { qCWarning(lcFileSystem) << "fileEquals: Failed to open " << fn1 << "or" << fn2; return false; } if (getSize(fn1) != getSize(fn2)) { return false; } const int BufferSize = 16 * 1024; char buffer1[BufferSize]; char buffer2[BufferSize]; do { int r = f1.read(buffer1, BufferSize); if (f2.read(buffer2, BufferSize) != r) { // this should normally not happen: the files are supposed to have the same size. return false; } if (r <= 0) { return true; } if (memcmp(buffer1, buffer2, r) != 0) { return false; } } while (true); return false; } time_t FileSystem::getModTime(const QString &filename) { csync_file_stat_t stat; qint64 result = -1; if (csync_vio_local_stat(filename.toUtf8().data(), &stat) != -1 && (stat.modtime != 0)) { result = stat.modtime; } else { qCWarning(lcFileSystem) << "Could not get modification time for" << filename << "with csync, using QFileInfo"; result = Utility::qDateTimeToTime_t(QFileInfo(filename).lastModified()); } return result; } bool FileSystem::setModTime(const QString &filename, time_t modTime) { struct timeval times[2]; times[0].tv_sec = times[1].tv_sec = modTime; times[0].tv_usec = times[1].tv_usec = 0; int rc = c_utimes(filename.toUtf8().data(), times); if (rc != 0) { qCWarning(lcFileSystem) << "Error setting mtime for" << filename << "failed: rc" << rc << ", errno:" << errno; return false; } return true; } bool FileSystem::fileChanged(const QString &fileName, qint64 previousSize, time_t previousMtime) { return getSize(fileName) != previousSize || getModTime(fileName) != previousMtime; } bool FileSystem::verifyFileUnchanged(const QString &fileName, qint64 previousSize, time_t previousMtime) { const qint64 actualSize = getSize(fileName); const time_t actualMtime = getModTime(fileName); if (actualSize != previousSize || actualMtime != previousMtime) { qCInfo(lcFileSystem) << "File" << fileName << "has changed:" << "size: " << previousSize << "<->" << actualSize << ", mtime: " << previousMtime << "<->" << actualMtime; return false; } return true; } #ifdef Q_OS_WIN static qint64 getSizeWithCsync(const QString &filename) { qint64 result = 0; csync_file_stat_t stat; if (csync_vio_local_stat(filename.toUtf8().data(), &stat) != -1) { result = stat.size; } else { qCWarning(lcFileSystem) << "Could not get size for" << filename << "with csync"; } return result; } #endif qint64 FileSystem::getSize(const QString &filename) { #ifdef Q_OS_WIN if (isLnkFile(filename)) { // Use csync to get the file size. Qt seems unable to get at it. return getSizeWithCsync(filename); } #endif return QFileInfo(filename).size(); } // Code inspired from Qt5's QDir::removeRecursively bool FileSystem::removeRecursively(const QString &path, const std::function &onDeleted, QStringList *errors) { bool allRemoved = true; QDirIterator di(path, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot); while (di.hasNext()) { di.next(); const QFileInfo &fi = di.fileInfo(); bool removeOk = false; // The use of isSymLink here is okay: // we never want to go into this branch for .lnk files bool isDir = fi.isDir() && !fi.isSymLink() && !FileSystem::isJunction(fi.absoluteFilePath()); if (isDir) { removeOk = removeRecursively(path + QLatin1Char('/') + di.fileName(), onDeleted, errors); // recursive } else { QString removeError; removeOk = FileSystem::remove(di.filePath(), &removeError); if (removeOk) { if (onDeleted) onDeleted(di.filePath(), false); } else { if (errors) { errors->append(QCoreApplication::translate("FileSystem", "Error removing '%1': %2") .arg(QDir::toNativeSeparators(di.filePath()), removeError)); } qCWarning(lcFileSystem) << "Error removing " << di.filePath() << ':' << removeError; } } if (!removeOk) allRemoved = false; } if (allRemoved) { allRemoved = QDir().rmdir(path); if (allRemoved) { if (onDeleted) onDeleted(path, true); } else { if (errors) { errors->append(QCoreApplication::translate("FileSystem", "Could not remove folder '%1'") .arg(QDir::toNativeSeparators(path))); } qCWarning(lcFileSystem) << "Error removing folder" << path; } } return allRemoved; } } // namespace OCC