diff --git a/src/libcommon/utility/types.h b/src/libcommon/utility/types.h index d70a96329..86d55716e 100644 --- a/src/libcommon/utility/types.h +++ b/src/libcommon/utility/types.h @@ -37,6 +37,8 @@ typedef std::string NodeId; typedef std::filesystem::path SyncPath; typedef std::filesystem::path::string_type SyncName; typedef std::filesystem::path::value_type SyncChar; +typedef std::filesystem::directory_entry DirectoryEntry; +typedef std::filesystem::directory_options DirectoryOptions; typedef std::variant SigValueType; @@ -344,6 +346,7 @@ typedef enum { IoErrorIsADirectory, IoErrorNoSuchFileOrDirectory, IoErrorResultOutOfRange, + IoErrorInvalidDirectoryIterator, IoErrorUnknown } IoError; @@ -360,8 +363,11 @@ struct ItemType { }; enum class AppStateKey { - // Adding a new key here requires to add it in insertDefaultAppState in parmsdbappstate.cpp and ideally testparmsdb.cpp - Unknown, // Used for initialization, will throw error if used - Test // To be removed after the implementation of the first key + // Adding a new key here requires to add it in insertDefaultAppState in parmsdbappstate.cpp + LastServerSelfRestart, + LastClientSelfRestart, + + Unknown, //!\ keep in last position (For tests) /!\\ Used for initialization, will throw error if used. }; + } // namespace KDC diff --git a/src/libcommonserver/io/iohelper.cpp b/src/libcommonserver/io/iohelper.cpp index e929dd367..486a85bae 100644 --- a/src/libcommonserver/io/iohelper.cpp +++ b/src/libcommonserver/io/iohelper.cpp @@ -131,6 +131,8 @@ std::string IoHelper::ioError2StdString(IoError ioError) noexcept { return "Result out of range"; case IoErrorSuccess: return "Success"; + case IoErrorInvalidDirectoryIterator: + return "Invalid directory iterator"; default: return "Unknown error"; } @@ -478,6 +480,34 @@ bool IoHelper::createDirectory(const SyncPath &path, IoError &ioError) noexcept return creationSuccess; } +bool IoHelper::deleteDirectory(const SyncPath &path, IoError &ioError) noexcept { + std::error_code ec; + std::filesystem::remove_all(path, ec); + ioError = stdError2ioError(ec); + return ioError == IoErrorSuccess; +} + +bool IoHelper::copyFileOrDirectory(const SyncPath &sourcePath, const SyncPath &destinationPath, IoError &ioError) noexcept { + std::error_code ec; + std::filesystem::copy(sourcePath, destinationPath, std::filesystem::copy_options::recursive, ec); + ioError = IoHelper::stdError2ioError(ec); + + return ioError == IoErrorSuccess; +} + +bool IoHelper::getDirectoryIterator(const SyncPath &path, bool recursive, IoError &ioError, + DirectoryIterator &iterator) noexcept { + iterator = DirectoryIterator(path, recursive, ioError); + return ioError == IoErrorSuccess; +} + +bool IoHelper::getDirectoryEntry(const SyncPath &path, IoError &ioError, DirectoryEntry &entry) noexcept { + std::error_code ec; + entry = std::filesystem::directory_entry(path, ec); + ioError = stdError2ioError(ec); + return ioError == IoErrorSuccess; +} + bool IoHelper::createSymlink(const SyncPath &targetPath, const SyncPath &path, IoError &ioError) noexcept { if (targetPath == path) { LOGW_DEBUG(Log::instance()->getLogger(), L"Cannot create symlink on itself - path=" << Path2WStr(path).c_str()); @@ -508,6 +538,89 @@ bool IoHelper::createSymlink(const SyncPath &targetPath, const SyncPath &path, I return ioError == IoErrorSuccess; } +// DirectoryIterator + +IoHelper::DirectoryIterator::DirectoryIterator(const SyncPath &directoryPath, bool recursive, IoError &ioError) + : _recursive(recursive), _directoryPath(directoryPath) { + std::error_code ec; + + _dirIterator = std::filesystem::begin( + std::filesystem::recursive_directory_iterator(directoryPath, DirectoryOptions::skip_permission_denied, ec)); + ioError = IoHelper::stdError2ioError(ec); +} + + +bool IoHelper::DirectoryIterator::next(DirectoryEntry &nextEntry, bool &endOfDirectory, IoError &ioError) { + std::error_code ec; + endOfDirectory = false; + + if (_invalid) { + ioError = IoErrorInvalidDirectoryIterator; + return true; + } + + if (_directoryPath == "") { + ioError = IoErrorInvalidArgument; + return false; + } + + if (_dirIterator == std::filesystem::end(std::filesystem::recursive_directory_iterator(_directoryPath, ec))) { + endOfDirectory = true; + ioError = IoErrorSuccess; + return true; + } + + if (!_recursive) { + disableRecursionPending(); + } + + if (!_firstElement) { + _dirIterator.increment(ec); + ioError = IoHelper::stdError2ioError(ec); + + if (ioError != IoErrorSuccess) { + _invalid = true; + return true; + } + + } else { + _firstElement = false; + } + + if (_dirIterator != std::filesystem::end(std::filesystem::recursive_directory_iterator(_directoryPath, ec))) { + ioError = IoHelper::stdError2ioError(ec); + + if (ioError != IoErrorSuccess) { + _invalid = true; + return true; + } + +#ifdef _WIN32 + // skip_permission_denied doesn't work on Windows + try { + bool dummy = _dirIterator->exists(); + (void)dummy; + nextEntry = *_dirIterator; + return true; + } catch (std::filesystem::filesystem_error &) { + _dirIterator.disable_recursion_pending(); + return next(nextEntry, endOfDirectory, ioError); + } + +#endif + nextEntry = *_dirIterator; + return true; + } else { + ioError = IoErrorSuccess; + endOfDirectory = true; + return true; + } +} + +void IoHelper::DirectoryIterator::disableRecursionPending() { + _dirIterator.disable_recursion_pending(); +} + #ifndef _WIN32 //See iohelper_win.cpp for the Windows implementation bool IoHelper::setRights(const SyncPath &path, bool read, bool write, bool exec, IoError &ioError) noexcept { diff --git a/src/libcommonserver/io/iohelper.h b/src/libcommonserver/io/iohelper.h index c7d43b11e..f4805d18b 100644 --- a/src/libcommonserver/io/iohelper.h +++ b/src/libcommonserver/io/iohelper.h @@ -28,7 +28,31 @@ namespace KDC { struct FileStat; + struct IoHelper { + public: + class DirectoryIterator { + public: + DirectoryIterator(const SyncPath &directoryPath, bool recursive, IoError &ioError); + + DirectoryIterator() = default; + //! Get the next directory entry. + /*! + \param nextEntry is set with the next directory entry. + \param ioError holds the error returned when an underlying OS API call fails. + \return true if no error occurred, false otherwise. + */ + bool next(DirectoryEntry &nextEntry, bool &endOfDirectory, IoError &ioError); + void disableRecursionPending(); + + private: + bool _recursive = false; + bool _firstElement = true; + bool _invalid = false; + SyncPath _directoryPath; + std::filesystem::recursive_directory_iterator _dirIterator; + }; + public: IoHelper(){}; @@ -209,6 +233,44 @@ struct IoHelper { */ static bool createDirectory(const SyncPath &path, IoError &ioError) noexcept; + //! Remove a directory located under the specified path. + /*! + \param path is the file system path of the directory to remove. + \param ioError holds the error returned when an underlying OS API call fails. + \return true if no unexpected error occurred, false otherwise. + */ + static bool deleteDirectory(const SyncPath &path, IoError &ioError) noexcept; + + //! Create a directory iterator for the specified path. The iterator can be used to iterate over the items in the directory. + /*! + \param path is the file system path of the directory to iterate over. + \param recursive is a boolean indicating whether the iterator should be recursive or not. + \param ioError holds the error returned when an underlying OS API call fails. + \param iterator is the directory iterator that is set with the directory iterator for the specified path. + \return true if no unexpected error occurred, false otherwise. + */ + static bool getDirectoryIterator(const SyncPath &path, bool recursive, IoError &ioError, + DirectoryIterator &iterator) noexcept; + + //! Create a directory entry for the specified path. + /*! + * \param path is the file system path of the directory entry to create. + * \param ioError holds the error returned when an underlying OS API call fails. + * \entry is the directory entry that is set with the directory entry for the specified path. + * \return true if no unexpected error occurred, false otherwise. + */ + static bool getDirectoryEntry(const SyncPath &path, IoError &ioError, DirectoryEntry &entry) noexcept; + + //! Copy the item indicated by `sourcePath` to the location indicated by `destinationPath`. + /*! + \param sourcePath is the file system path of the item to copy. + \param destinationPath is the file system path of the location to copy the item to. + \param ioError holds the error associated to a failure of the underlying OS API call, if any. + \return true if no unexpected error occurred, false otherwise. + */ + static bool copyFileOrDirectory(const SyncPath &sourcePath, const SyncPath &destinationPath, IoError &ioError) noexcept; + + #ifdef __APPLE__ // From `man xattr`: // Extended attributes are arbitrary metadata stored with a file, but separate from the @@ -268,7 +330,7 @@ struct IoHelper { \return true if no unexpected error occurred, false otherwise. */ static bool getRights(const SyncPath &path, bool &read, bool &write, bool &exec, IoError &ioError) noexcept; - + //! Set the rights of the item indicated by `path`. /*! \param path is the file system path of the item. @@ -281,6 +343,8 @@ struct IoHelper { static bool setRights(const SyncPath &path, bool read, bool write, bool exec, IoError &ioError) noexcept; protected: + friend class DirectoryIterator; + // These functions default to the std::filesystem functions. // They can be modified in tests. static std::function _isDirectory; diff --git a/src/libcommonserver/io/iohelper_win.cpp b/src/libcommonserver/io/iohelper_win.cpp index dcf2fe5ee..03ebad027 100644 --- a/src/libcommonserver/io/iohelper_win.cpp +++ b/src/libcommonserver/io/iohelper_win.cpp @@ -62,8 +62,10 @@ IoError dWordError2ioError(DWORD error) noexcept { case ERROR_FILE_NOT_FOUND: case ERROR_INVALID_DRIVE: case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_NAME: return IoErrorNoSuchFileOrDirectory; default: + LOG_WARN(Log::instance()->getLogger(), L"Unknown IO error - error=" << error); return IoErrorUnknown; } } // namespace @@ -549,7 +551,6 @@ bool IoHelper::getRights(const SyncPath &path, bool &read, bool &write, bool &ex LOGW_WARN(logger(), L"Failed to get permissions: " << Utility::formatStdError(path, ec).c_str()); return _isExpectedError(ioError); } - read = ((perms & std::filesystem::perms::owner_read) != std::filesystem::perms::none); write = ((perms & std::filesystem::perms::owner_write) != std::filesystem::perms::none); exec = ((perms & std::filesystem::perms::owner_exec) != std::filesystem::perms::none); diff --git a/src/libparms/CMakeLists.txt b/src/libparms/CMakeLists.txt index 04b16c817..668cee7e5 100644 --- a/src/libparms/CMakeLists.txt +++ b/src/libparms/CMakeLists.txt @@ -18,7 +18,7 @@ set(parms_SRCS db/error.h db/error.cpp db/migrationselectivesync.h db/migrationselectivesync.cpp db/uploadsessiontoken.h db/uploadsessiontoken.cpp - db/parmsdbappstate.h db/parmsdbappstate.cpp + db/parmsdbappstate.cpp ) if (APPLE) diff --git a/src/libparms/db/parmsdb.cpp b/src/libparms/db/parmsdb.cpp index 9552da45d..0e52dea10 100644 --- a/src/libparms/db/parmsdb.cpp +++ b/src/libparms/db/parmsdb.cpp @@ -500,32 +500,6 @@ #define SELECT_ALL_MIGRATION_SELECTIVESYNC_REQUEST_ID "select_migration_selectivesync" #define SELECT_ALL_MIGRATION_SELECTIVESYNC_REQUEST "SELECT syncDbId, path, type FROM migration_selectivesync;" - -// -// self_restarter -// - -#define CREATE_SELF_RESTARTER_TABLE_ID "create_self_restarter" -#define CREATE_SELF_RESTARTER_TABLE \ - "CREATE TABLE IF NOT EXISTS self_restarter(" \ - "lastClientRestart INTEGER," \ - "lastServerRestart INTEGER);" - -#define INSERT_SELF_RESTARTER_REQUEST_ID "insert_self_restarter" -#define INSERT_SELF_RESTARTER_REQUEST \ - "INSERT INTO self_restarter (lastClientRestart, lastServerRestart) " \ - "VALUES (?1, ?2);" - -#define SELECT_SELF_RESTARTER_REQUEST_ID "select_self_restarter" -#define SELECT_SELF_RESTARTER_REQUEST "SELECT lastClientRestart, lastServerRestart FROM self_restarter;" - -#define UPDATE_SELF_RESTARTER_CLIENT_REQUEST_ID "update_self_restarter_client_time" -#define UPDATE_SELF_RESTARTER_CLIENT_REQUEST "UPDATE self_restarter SET lastClientRestart=?1;" - -#define UPDATE_SELF_RESTARTER_SERVER_REQUEST_ID "update_self_restarter_server_time" -#define UPDATE_SELF_RESTARTER_SERVER_REQUEST "UPDATE self_restarter SET lastServerRestart=?1;" - - namespace KDC { std::shared_ptr ParmsDb::_instance = nullptr; @@ -561,32 +535,6 @@ ParmsDb::ParmsDb(const std::filesystem::path &dbPath, const std::string &version LOG_INFO(_logger, "ParmsDb initialization done"); } -bool ParmsDb::insertDefaultSelfRestarterData() { - const std::scoped_lock lock(_mutex); - - bool found = false; - if (!queryNext(SELECT_SELF_RESTARTER_REQUEST_ID, found)) { - LOG_WARN(_logger, "Error getting query result: " << SELECT_SELF_RESTARTER_REQUEST_ID); - return false; - } - if (found) { - return true; - } - - int errId = 0; - std::string error; - - ASSERT(queryResetAndClearBindings(INSERT_SELF_RESTARTER_REQUEST_ID)); - ASSERT(queryBindValue(INSERT_SELF_RESTARTER_REQUEST_ID, 1, 0)); - ASSERT(queryBindValue(INSERT_SELF_RESTARTER_REQUEST_ID, 2, 0)); - - if (!queryExec(INSERT_SELF_RESTARTER_REQUEST_ID, errId, error)) { - LOG_WARN(_logger, "Error running query: " << INSERT_SELF_RESTARTER_REQUEST_ID); - return false; - } - return true; -} - bool ParmsDb::insertDefaultParameters() { const std::scoped_lock lock(_mutex); @@ -929,22 +877,9 @@ bool ParmsDb::create(bool &retry) { return sqlFail(CREATE_ERROR_TABLE_ID, error); } queryFree(CREATE_ERROR_TABLE_ID); - - // self restarter - ASSERT(queryCreate(CREATE_SELF_RESTARTER_TABLE_ID)); - if (!queryPrepare(CREATE_SELF_RESTARTER_TABLE_ID, CREATE_SELF_RESTARTER_TABLE, false, errId, error)) { - queryFree(CREATE_SELF_RESTARTER_TABLE_ID); - return sqlFail(CREATE_SELF_RESTARTER_TABLE_ID, error); - } - - if (!queryExec(CREATE_SELF_RESTARTER_TABLE_ID, errId, error)) { - queryFree(CREATE_SELF_RESTARTER_TABLE_ID); - return sqlFail(CREATE_SELF_RESTARTER_TABLE_ID, error); - } - queryFree(CREATE_SELF_RESTARTER_TABLE_ID); - + // app state - createAppState(); + if(!createAppState()) return false; // Migration old selectivesync table ASSERT(queryCreate(CREATE_MIGRATION_SELECTIVESYNC_TABLE_ID)); @@ -1300,34 +1235,9 @@ bool ParmsDb::prepare() { queryFree(SELECT_ALL_MIGRATION_SELECTIVESYNC_REQUEST_ID); return sqlFail(SELECT_ALL_MIGRATION_SELECTIVESYNC_REQUEST_ID, error); } - - // self restarter - ASSERT(queryCreate(SELECT_SELF_RESTARTER_REQUEST_ID)); - if (!queryPrepare(SELECT_SELF_RESTARTER_REQUEST_ID, SELECT_SELF_RESTARTER_REQUEST, false, errId, error)) { - queryFree(SELECT_SELF_RESTARTER_REQUEST_ID); - return sqlFail(SELECT_SELF_RESTARTER_REQUEST_ID, error); - } - - ASSERT(queryCreate(UPDATE_SELF_RESTARTER_SERVER_REQUEST_ID)); - if (!queryPrepare(UPDATE_SELF_RESTARTER_SERVER_REQUEST_ID, UPDATE_SELF_RESTARTER_SERVER_REQUEST, false, errId, error)) { - queryFree(UPDATE_SELF_RESTARTER_SERVER_REQUEST_ID); - return sqlFail(UPDATE_SELF_RESTARTER_SERVER_REQUEST_ID, error); - } - - ASSERT(queryCreate(UPDATE_SELF_RESTARTER_CLIENT_REQUEST_ID)); - if (!queryPrepare(UPDATE_SELF_RESTARTER_CLIENT_REQUEST_ID, UPDATE_SELF_RESTARTER_CLIENT_REQUEST, false, errId, error)) { - queryFree(UPDATE_SELF_RESTARTER_CLIENT_REQUEST_ID); - return sqlFail(UPDATE_SELF_RESTARTER_CLIENT_REQUEST_ID, error); - } - - ASSERT(queryCreate(INSERT_SELF_RESTARTER_REQUEST_ID)); - if (!queryPrepare(INSERT_SELF_RESTARTER_REQUEST_ID, INSERT_SELF_RESTARTER_REQUEST, false, errId, error)) { - queryFree(INSERT_SELF_RESTARTER_REQUEST_ID); - return sqlFail(INSERT_SELF_RESTARTER_REQUEST_ID, error); - } - + // App state - prepareAppState(); + if(!prepareAppState()) return false; if (!initData()) { LOG_WARN(_logger, "Error in initParameters"); @@ -1407,21 +1317,7 @@ bool ParmsDb::upgrade(const std::string &fromVersion, const std::string & /*toVe if (CommonUtility::isVersionLower(dbFromVersionNumber, "3.6.1")) { LOG_DEBUG(_logger, "Upgrade < 3.6.1 DB"); - - queryFree(CREATE_SELF_RESTARTER_TABLE_ID); - ASSERT(queryCreate(CREATE_SELF_RESTARTER_TABLE_ID)); - if (!queryPrepare(CREATE_SELF_RESTARTER_TABLE_ID, CREATE_SELF_RESTARTER_TABLE, false, errId, error)) { - queryFree(CREATE_SELF_RESTARTER_TABLE_ID); - return sqlFail(CREATE_SELF_RESTARTER_TABLE_ID, error); - } - - if (!queryExec(CREATE_SELF_RESTARTER_TABLE_ID, errId, error)) { - queryFree(CREATE_SELF_RESTARTER_TABLE_ID); - return sqlFail(CREATE_SELF_RESTARTER_TABLE_ID, error); - } - queryFree(CREATE_SELF_RESTARTER_TABLE_ID); - - createAppState(); + if(createAppState()) return false; } return true; @@ -1445,16 +1341,12 @@ bool ParmsDb::initData() { return false; } - if (!insertDefaultSelfRestarterData()) { - LOG_WARN(_logger, "Error in insertDefaultSelfRestarterData"); - return false; - } - if (!insertDefaultAppState()) { LOG_WARN(_logger, "Error in insertDefaultAppState"); return false; } + // Update exclusion templates if (!updateExclusionTemplates()) { LOG_WARN(_logger, "Error in updateExclusionTemplates"); @@ -3300,89 +3192,4 @@ bool ParmsDb::selectAllMigrationSelectiveSync(std::vector(std::chrono::system_clock::now()).time_since_epoch().count(); - } - - ASSERT(queryResetAndClearBindings(UPDATE_SELF_RESTARTER_SERVER_REQUEST_ID)); - ASSERT(queryBindValue(UPDATE_SELF_RESTARTER_SERVER_REQUEST_ID, 1, lastServertRestartTime)); - - int errId = 0; - std::string error; - - if (!queryExec(UPDATE_SELF_RESTARTER_SERVER_REQUEST_ID, errId, error)) { - LOG_WARN(_logger, "Error running query: " << UPDATE_SELF_RESTARTER_SERVER_REQUEST_ID); - return false; - } - ASSERT(queryResetAndClearBindings(UPDATE_SELF_RESTARTER_SERVER_REQUEST_ID)); - - return true; -} - -bool ParmsDb::updateLastClientSelfRestartTime(int64_t lastClientRestartTime) { - const std::scoped_lock lock(_mutex); - - if (lastClientRestartTime == -1) { - lastClientRestartTime = - std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); - } - - ASSERT(queryResetAndClearBindings(UPDATE_SELF_RESTARTER_CLIENT_REQUEST_ID)); - ASSERT(queryBindValue(UPDATE_SELF_RESTARTER_CLIENT_REQUEST_ID, 1, lastClientRestartTime)); - - int errId = 0; - std::string error; - - if (!queryExec(UPDATE_SELF_RESTARTER_CLIENT_REQUEST_ID, errId, error)) { - LOG_WARN(_logger, "Error running query: " << UPDATE_SELF_RESTARTER_CLIENT_REQUEST_ID); - return false; - } - ASSERT(queryResetAndClearBindings(UPDATE_SELF_RESTARTER_CLIENT_REQUEST_ID)); - - return true; -} - } // namespace KDC diff --git a/src/libparms/db/parmsdb.h b/src/libparms/db/parmsdb.h index 7b2c8a735..9878ca97f 100644 --- a/src/libparms/db/parmsdb.h +++ b/src/libparms/db/parmsdb.h @@ -122,15 +122,9 @@ class PARMS_EXPORT ParmsDb : public Db { bool insertMigrationSelectiveSync(const MigrationSelectiveSync &migrationSelectiveSync); bool selectAllMigrationSelectiveSync(std::vector &migrationSelectiveSyncList); - bool selectLastServerSelfRestartTime(int64_t &lastServerRestartTime); - bool selectLastClientSelfRestartTime(int64_t &lastClientRestartTime); - bool updateLastServerSelfRestartTime(int64_t lastServerRestartTime = -1 /* -1 means now*/); - bool updateLastClientSelfRestartTime(int64_t lastClientRestartTime = -1 /* -1 means now*/); - bool selectAppState(AppStateKey key, std::string &value, bool& found); bool updateAppState(AppStateKey key, const std::string &value, bool& found); // update or insert - private: static std::shared_ptr _instance; bool _test; @@ -138,8 +132,8 @@ class PARMS_EXPORT ParmsDb : public Db { ParmsDb(const std::filesystem::path &dbPath, const std::string &version, bool autoDelete, bool test); bool insertDefaultParameters(); - bool insertDefaultSelfRestarterData(); bool insertDefaultAppState(); + bool insertAppState(AppStateKey key, const std::string &value); bool updateExclusionTemplates(); bool createAppState(); diff --git a/src/libparms/db/parmsdbappstate.cpp b/src/libparms/db/parmsdbappstate.cpp index 41c06e80c..3382a6313 100644 --- a/src/libparms/db/parmsdbappstate.cpp +++ b/src/libparms/db/parmsdbappstate.cpp @@ -17,9 +17,22 @@ */ #include "parmsdb.h" -#include "parmsdbappstate.h" #include "libcommonserver/utility/asserts.h" +constexpr char CREATE_APP_STATE_TABLE_ID[] = "create_app_state"; +constexpr char CREATE_APP_STATE_TABLE[] = "CREATE TABLE IF NOT EXISTS app_state(key INTEGER PRIMARY KEY, value TEXT);"; + +constexpr char INSERT_APP_STATE_REQUEST_ID[] = "insert_app_state"; +constexpr char INSERT_APP_STATE_REQUEST[] = "INSERT INTO app_state (key, value) VALUES (?1, ?2);"; + +constexpr char SELECT_APP_STATE_REQUEST_ID[] = "select_value_from_key"; +constexpr char SELECT_APP_STATE_REQUEST[] = "SELECT value FROM app_state WHERE key=?1;"; + +constexpr char UPDATE_APP_STATE_REQUEST_ID[] = "update_value_with_key"; +constexpr char UPDATE_APP_STATE_REQUEST[] = "UPDATE app_state SET value=?2 WHERE key=?1;"; + +constexpr char APP_STATE_KEY_DEFAULT_LastServerSelfRestart[] = "0"; +constexpr char APP_STATE_KEY_DEFAULT_LastClientSelfRestart[] = "0"; namespace KDC { @@ -65,13 +78,25 @@ bool ParmsDb::prepareAppState() { } bool ParmsDb::insertDefaultAppState() { + if (!insertAppState(AppStateKey::LastServerSelfRestart, APP_STATE_KEY_DEFAULT_LastServerSelfRestart)) { + return false; + } + + if (!insertAppState(AppStateKey::LastClientSelfRestart, APP_STATE_KEY_DEFAULT_LastClientSelfRestart)) { + return false; + } + + return true; +} + +bool ParmsDb::insertAppState(AppStateKey key, const std::string &value) { const std::scoped_lock lock(_mutex); int errId = 0; std::string error; bool found = false; ASSERT(queryResetAndClearBindings(SELECT_APP_STATE_REQUEST_ID)); - ASSERT(queryBindValue(SELECT_APP_STATE_REQUEST_ID, 1, static_cast(AppStateKey::Test))); + ASSERT(queryBindValue(SELECT_APP_STATE_REQUEST_ID, 1, static_cast(key))); if (!queryNext(SELECT_APP_STATE_REQUEST_ID, found)) { LOG_WARN(_logger, "Error getting query result: " << SELECT_APP_STATE_REQUEST_ID); return false; @@ -80,8 +105,8 @@ bool ParmsDb::insertDefaultAppState() { ASSERT(queryResetAndClearBindings(SELECT_APP_STATE_REQUEST_ID)); if (!found) { ASSERT(queryResetAndClearBindings(INSERT_APP_STATE_REQUEST_ID)); - ASSERT(queryBindValue(INSERT_APP_STATE_REQUEST_ID, 1, static_cast(AppStateKey::Test))); - ASSERT(queryBindValue(INSERT_APP_STATE_REQUEST_ID, 2, APP_STATE_KEY_DEFAULT_Test)); + ASSERT(queryBindValue(INSERT_APP_STATE_REQUEST_ID, 1, static_cast(key))); + ASSERT(queryBindValue(INSERT_APP_STATE_REQUEST_ID, 2, value)); if (!queryExec(INSERT_APP_STATE_REQUEST_ID, errId, error)) { LOG_WARN(_logger, "Error running query: " << INSERT_APP_STATE_REQUEST_ID); return false; @@ -123,7 +148,7 @@ bool ParmsDb::updateAppState(AppStateKey key, const std::string &value, bool &fo } const std::scoped_lock lock(_mutex); - if (found) { + if (found) { ASSERT(queryResetAndClearBindings(UPDATE_APP_STATE_REQUEST_ID)); ASSERT(queryBindValue(UPDATE_APP_STATE_REQUEST_ID, 1, static_cast(key))); ASSERT(queryBindValue(UPDATE_APP_STATE_REQUEST_ID, 2, value)); @@ -134,6 +159,5 @@ bool ParmsDb::updateAppState(AppStateKey key, const std::string &value, bool &fo ASSERT(queryResetAndClearBindings(UPDATE_APP_STATE_REQUEST_ID)); } return true; -} - +} } // namespace KDC \ No newline at end of file diff --git a/src/libparms/db/parmsdbappstate.h b/src/libparms/db/parmsdbappstate.h deleted file mode 100644 index ec891f8c1..000000000 --- a/src/libparms/db/parmsdbappstate.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Infomaniak kDrive - Desktop - * Copyright (C) 2023-2024 Infomaniak Network SA - * - * 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 3 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#define CREATE_APP_STATE_TABLE_ID "create_app_state" -#define CREATE_APP_STATE_TABLE \ - "CREATE TABLE IF NOT EXISTS app_state(" \ - "key INTEGER PRIMARY KEY," \ - "value TEXT);" - -#define INSERT_APP_STATE_REQUEST_ID "insert_app_state" -#define INSERT_APP_STATE_REQUEST \ - "INSERT INTO app_state (key, value) " \ - "VALUES (?1, ?2);" - -#define SELECT_APP_STATE_REQUEST_ID "select_value_from_key" -#define SELECT_APP_STATE_REQUEST "SELECT value FROM app_state WHERE key=?1;" - -#define UPDATE_APP_STATE_REQUEST_ID "update_value_with_key" -#define UPDATE_APP_STATE_REQUEST "UPDATE app_state SET value=?2 WHERE key=?1;" - -#define APP_STATE_KEY_DEFAULT_Test "Test" \ No newline at end of file diff --git a/src/server/appserver.cpp b/src/server/appserver.cpp index bfed98cf7..f56c79970 100644 --- a/src/server/appserver.cpp +++ b/src/server/appserver.cpp @@ -98,6 +98,7 @@ static const char optionsC[] = static const QString showSynthesisMsg = "showSynthesis"; static const QString showSettingsMsg = "showSettings"; +static const QString crashMsg = SharedTools::QtSingleApplication::tr("kDrive application will close due to a fatal error."); // Helpers for displaying messages. Note that there is no console on Windows. #ifdef Q_OS_WIN @@ -317,16 +318,28 @@ AppServer::AppServer(int &argc, char **argv) // Check last crash to avoid crash loop if (_crashRecovered) { + bool found = false; LOG_WARN(_logger, "Server auto restart after a crash."); if (serverCrashedRecently()) { LOG_FATAL(_logger, "Server crashed twice in a short time, exiting"); - KDC::ParmsDb::instance()->updateLastServerSelfRestartTime(); - QMessageBox::warning(0, QString(APPLICATION_NAME), tr("kDrive application crashed!"), QMessageBox::Ok); - KDC::ParmsDb::instance()->updateLastServerSelfRestartTime(0); + QMessageBox::warning(0, QString(APPLICATION_NAME), crashMsg, QMessageBox::Ok); + if (!KDC::ParmsDb::instance()->updateAppState(AppStateKey::LastServerSelfRestart, "0", found) || !found) { + LOG_WARN(_logger, "Error in ParmsDb::updateAppState"); + addError(Error(ERRID, ExitCodeDbError, ExitCauseDbEntryNotFound)); + throw std::runtime_error("Failed to update last server self restart."); + } QTimer::singleShot(0, this, quit); return; } - KDC::ParmsDb::instance()->updateLastServerSelfRestartTime(); + long timestamp = + std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + std::string timestampStr = std::to_string(timestamp); + KDC::ParmsDb::instance()->updateAppState(AppStateKey::LastServerSelfRestart, timestampStr, found); + if (!KDC::ParmsDb::instance()->updateAppState(AppStateKey::LastServerSelfRestart, timestampStr, found) || !found) { + LOG_WARN(_logger, "Error in ParmsDb::updateAppState"); + addError(Error(ERRID, ExitCodeDbError, ExitCauseDbEntryNotFound)); + throw std::runtime_error("Failed to update last server self restart."); + } } // Start client @@ -2051,13 +2064,28 @@ void AppServer::onRestartClientReceived() { // Check last start time if (clientCrashedRecently()) { LOG_FATAL(_logger, "Client crashed twice in a short time, exiting"); - KDC::ParmsDb::instance()->updateLastClientSelfRestartTime(0); - QMessageBox::warning(0, QString(APPLICATION_NAME), tr("kDrive application crashed"), QMessageBox::Ok); + bool found = false; + if (!KDC::ParmsDb::instance()->updateAppState(AppStateKey::LastClientSelfRestart, "0", found) || !found) { + addError(Error(ERRID, ExitCodeDbError, ExitCauseDbEntryNotFound)); + LOG_WARN(_logger, "Error in ParmsDb::selectAppState"); + } + QMessageBox::warning(0, QString(APPLICATION_NAME), crashMsg, QMessageBox::Ok); QTimer::singleShot(0, this, &AppServer::quit); return; } else { CommServer::instance()->setHasQuittedProperly(false); - KDC::ParmsDb::instance()->updateLastClientSelfRestartTime(); + long timestamp = + std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + std::string timestampStr = std::to_string(timestamp); + bool found = false; + if (!KDC::ParmsDb::instance()->updateAppState(AppStateKey::LastClientSelfRestart, timestampStr, found) || !found) { + addError(Error(ERRID, ExitCodeDbError, ExitCauseDbEntryNotFound)); + LOG_WARN(_logger, "Error in ParmsDb::selectAppState"); + QMessageBox::warning(0, QString(APPLICATION_NAME), crashMsg, QMessageBox::Ok); + QTimer::singleShot(0, this, &AppServer::quit); + return; + } + if (!startClient()) { LOG_WARN(_logger, "Error in startClient"); } @@ -2847,8 +2875,27 @@ bool AppServer::serverCrashedRecently(int seconds) { const int64_t nowSeconds = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); - int64_t lastServerCrash; - KDC::ParmsDb::instance()->selectLastServerSelfRestartTime(lastServerCrash); + int64_t lastServerCrash = 0; + std::string lastServerCrashStr; + bool found = false; + + if (!KDC::ParmsDb::instance()->selectAppState(AppStateKey::LastServerSelfRestart, lastServerCrashStr, found) || !found) { + addError(Error(ERRID, ExitCodeDbError, ExitCauseDbEntryNotFound)); + LOG_WARN(_logger, "Error in ParmsDb::selectAppState"); + return false; + } + + if (lastServerCrashStr.empty()) { + return false; + } + + try { + lastServerCrash = std::stoll(lastServerCrashStr); + } catch (const std::invalid_argument &e) { + LOG_WARN(_logger, "Error in std::stoll: " << e.what()); + return false; + } + const auto diff = nowSeconds - lastServerCrash; if (diff > seconds) { return false; @@ -2863,7 +2910,26 @@ bool AppServer::clientCrashedRecently(int seconds) { std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); int64_t lastClientCrash = 0; - KDC::ParmsDb::instance()->selectLastClientSelfRestartTime(lastClientCrash); + std::string lastClientCrashStr; + bool found = false; + + if (!KDC::ParmsDb::instance()->selectAppState(AppStateKey ::LastClientSelfRestart, lastClientCrashStr, found) || !found) { + addError(Error(ERRID, ExitCodeDbError, ExitCauseDbEntryNotFound)); + LOG_WARN(_logger, "Error in ParmsDb::selectAppState"); + return false; + } + + if (lastClientCrashStr.empty()) { + return false; + } + + try { + lastClientCrash = std::stoll(lastClientCrashStr); + } catch (const std::invalid_argument &e) { + LOG_WARN(_logger, "Error in std::stoll: " << e.what()); + return false; + } + const auto diff = nowSeconds - lastClientCrash; if (diff > seconds) { return false; diff --git a/test/libcommonserver/CMakeLists.txt b/test/libcommonserver/CMakeLists.txt index 85d165039..23bc96bee 100644 --- a/test/libcommonserver/CMakeLists.txt +++ b/test/libcommonserver/CMakeLists.txt @@ -17,7 +17,7 @@ set(testcommon_SRCS db/testdb.h db/testdb.cpp # io io/testio.h io/testio.cpp io/testgetitemtype.cpp io/testgetfilesize.cpp io/testcheckifpathexists.cpp io/testgetnodeid.cpp io/testgetfilestat.cpp io/testisfileaccessible.cpp io/testfilechanged.cpp - io/testcheckifisdirectory.cpp io/testcreatesymlink.cpp io/testcheckifdehydrated.cpp io/testchecksetgetrights.cpp + io/testcheckifisdirectory.cpp io/testcreatesymlink.cpp io/testcheckifdehydrated.cpp io/testcheckdirectoryiterator.cpp io/testchecksetgetrights.cpp ) if (USE_OUR_OWN_SQLITE3) diff --git a/test/libcommonserver/io/testcheckdirectoryiterator.cpp b/test/libcommonserver/io/testcheckdirectoryiterator.cpp new file mode 100644 index 000000000..cb70b59f6 --- /dev/null +++ b/test/libcommonserver/io/testcheckdirectoryiterator.cpp @@ -0,0 +1,330 @@ +/* + * Infomaniak kDrive - Desktop + * Copyright (C) 2023-2024 Infomaniak Network SA + * + * 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 3 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testio.h" + +#include +#include "libcommonserver/utility/utility.h" +#include "libcommonserver/utility/testutility.h" + + +using namespace CppUnit; + +namespace KDC { + +void TestIo::testCheckDirectoryIterator() { +#ifdef _WIN32 + Utility::init(); // Initialize the utility library, needed to access/change the permissions on Windows +#endif + testCheckDirectoryIteratorNonExistingPath(); + testCheckDirectoryIteratorExistingPath(); + testCheckDirectoryRecursive(); + testCheckDirectoryIteratotNextAfterEndOfDir(); + testCheckDirectoryIteratorUnexpectedDelete(); + testCheckDirectoryIteratorPermission(); + testCheckDirectoryPermissionLost(); +} + +void TestIo::testCheckDirectoryIteratorNonExistingPath() { + // Check that the directory iterator returns an error when the path does not exist + { + IoError error; + const IoHelper::DirectoryIterator it("C:\\nonexistingpath", false, error); + + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorNoSuchFileOrDirectory, error); + } + + // Check that the directory iterator returns an error when the path syntax is invalid + { + IoError error; + const IoHelper::DirectoryIterator it("C:\\nonexistingpath\\*\\", false, error); + + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorNoSuchFileOrDirectory, error); + } +} + +void TestIo::testCheckDirectoryIteratorExistingPath() { + TemporaryDirectory tempDir; + + // Create test empty directory + const SyncPath emptyDir = tempDir.path / "chekDirIt/empty_dir"; + std::filesystem::create_directories(emptyDir); + + // Create test directory with one file + SyncPath oneFileDir = tempDir.path / "chekDirIt/oneFile_dir"; + std::filesystem::create_directories(oneFileDir); + std::ofstream file(oneFileDir / "oneFile.txt"); + file << "oneFile"; + file.close(); + + // Create test directory with one directory + std::filesystem::create_directories(tempDir.path / "chekDirIt/oneDir_dir/testDir1"); + + // Check that the directory iterator is valid when the path is an empty directory and return EOF + { + IoError error; + + IoHelper::DirectoryIterator it(emptyDir, false, error); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorSuccess, error); + + DirectoryEntry entry; + bool endOfDirectory = false; + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, error)); + CPPUNIT_ASSERT(endOfDirectory); + } + + // Check that the directory iterator is valid when the path is a directory with one file and return the file on first call + { + const SyncPath directoryWithOneFile = tempDir.path / "chekDirIt/oneFile_dir"; + + IoError error; + IoHelper::DirectoryIterator it(directoryWithOneFile, false, error); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorSuccess, error); + + DirectoryEntry entry; + bool endOfDirectory = false; + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, error)); + CPPUNIT_ASSERT(!endOfDirectory); + + CPPUNIT_ASSERT_EQUAL(directoryWithOneFile / "oneFile.txt", entry.path()); + + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, error)); + CPPUNIT_ASSERT(endOfDirectory); + } + + // Check that the directory iterator is valid when the path is a directory with one child directory + { + const SyncPath directoryWithOneChildDirectory = tempDir.path / "chekDirIt/oneDir_dir"; + + IoError error; + IoHelper::DirectoryIterator it(directoryWithOneChildDirectory, false, error); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorSuccess, error); + + DirectoryEntry entry; + bool endOfDirectory = false; + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, error)); + CPPUNIT_ASSERT(!endOfDirectory); + + CPPUNIT_ASSERT_EQUAL(directoryWithOneChildDirectory / "testDir1", entry.path()); + } +} + +void TestIo::testCheckDirectoryRecursive(void) { + TemporaryDirectory tempDir; + + // Create test directory with 4 directories with 1 file each + SyncPath recursiveDir = tempDir.path / "chekDirIt/recursive_dir"; + for (int i = 0; i < 4; ++i) { + SyncPath childDir = recursiveDir / ("childDir_" + std::to_string(i)); + std::filesystem::create_directories(childDir); + std::ofstream file(childDir / "file.txt"); + file << "file"; + file.close(); + } + + // Check that the directory iterator do not search recursively when the recursive flag is false + { + IoError error; + IoHelper::DirectoryIterator it(recursiveDir, false, error); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorSuccess, error); + + DirectoryEntry entry; + bool endOfDirectory = false; + + for (int i = 0; i < 4; i++) { + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, error)); + CPPUNIT_ASSERT(!endOfDirectory); + } + + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, error)); + CPPUNIT_ASSERT(endOfDirectory); + } + + // Check that the directory iterator searches recursively when the recursive flag is true + { + IoError error; + IoHelper::DirectoryIterator it(recursiveDir, true, error); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorSuccess, error); + + bool endOfDirectory = false; + DirectoryEntry entry; + + for (int i = 0; i < 8; i++) { + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, error)); + CPPUNIT_ASSERT(!endOfDirectory); + } + + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, error)); + CPPUNIT_ASSERT(endOfDirectory); + } +} + +void TestIo::testCheckDirectoryIteratotNextAfterEndOfDir() { + // Create test directory with one file + TemporaryDirectory tempDir; + SyncPath oneFileDir = tempDir.path / "chekDirIt/oneFile_dir"; + std::filesystem::create_directories(oneFileDir); + std::ofstream file(oneFileDir / "oneFile.txt"); + file << "oneFile"; + file.close(); + + // Check that the directory iterator returns an EOF on everycall to next after EOF + { + IoError error; + IoHelper::DirectoryIterator it(oneFileDir, false, error); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorSuccess, error); + + // Read the only file in the directory + bool endOfDirectory = false; + DirectoryEntry entry; + + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, error)); + CPPUNIT_ASSERT(!endOfDirectory); + + // Expecting EOF + for (int i = 0; i < 3; i++) { + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, error)); + CPPUNIT_ASSERT(endOfDirectory); + } + } +} + +void TestIo::testCheckDirectoryIteratorPermission() { + { + // Check that the directory iterator skips each directory with no permission + TemporaryDirectory tempDir; + const SyncPath noPermissionDir = tempDir.path / "chekDirIt/noPermission"; + const SyncPath noPermissionFile = noPermissionDir / "file.txt"; + + IoError ioError = IoErrorSuccess; + + CPPUNIT_ASSERT(IoHelper::createDirectory(noPermissionDir.parent_path(), ioError)); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorSuccess, ioError); + CPPUNIT_ASSERT(IoHelper::createDirectory(noPermissionDir, ioError)); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorSuccess, ioError); + + std::ofstream file(noPermissionFile); + file << "file"; + file.close(); + + + bool result = IoHelper::setRights(noPermissionDir, false, false, false, ioError); + result &= ioError == IoErrorSuccess; + if (!result) { + IoHelper::setRights(noPermissionDir, true, true, true, ioError); + CPPUNIT_ASSERT(false /*setRights failed*/); + } + + IoHelper::DirectoryIterator it(noPermissionDir, false, ioError); + + DirectoryEntry entry; + bool endOfDirectory = false; + bool success = it.next(entry, endOfDirectory, ioError); + + IoHelper::setRights(noPermissionDir, true, true, true, ioError); + + + CPPUNIT_ASSERT(success); + CPPUNIT_ASSERT(endOfDirectory); + } +} + +void TestIo::testCheckDirectoryIteratorUnexpectedDelete() { + TemporaryDirectory tempDir; + + // Create test directory with 5 subdirectories + const SyncPath path = tempDir.path.string() + "\\chekDirIt\\IteratorUnexpectedDelete"; + std::string subDir = path.string(); + + for (int i = 0; i < 5; i++) { + subDir += "/subDir" + std::to_string(i); + } + + std::filesystem::create_directories(subDir); + + // Check that the directory iterator is consistent when a parent directory is deleted + { + IoError ioError = IoErrorSuccess; + IoHelper::DirectoryIterator it(path, true, ioError); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorSuccess, ioError); + + DirectoryEntry entry; + bool endOfDirectory = false; + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, ioError)); + CPPUNIT_ASSERT(!endOfDirectory); + + std::filesystem::remove_all(path); + + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, ioError)); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorNoSuchFileOrDirectory, ioError); + + CPPUNIT_ASSERT(it.next(entry, endOfDirectory, ioError)); + CPPUNIT_ASSERT_EQUAL(IoError::IoErrorInvalidDirectoryIterator, ioError); + } +} + +void TestIo::testCheckDirectoryPermissionLost(void) { + const TemporaryDirectory temporaryDirectory; + const SyncPath chekDirItDir = temporaryDirectory.path / "chekDirIt"; + const SyncPath permLostRoot = chekDirItDir / "permissionLost"; + const SyncPath subDir = permLostRoot / "subDir1"; + const SyncPath filePath = subDir / "file.txt"; + + std::filesystem::create_directories(subDir); + std::ofstream file(filePath); + file << "file"; + file.close(); + + // Check that the directory iterator is consistent when a parent directory lose permission + { + IoError ioError = IoErrorSuccess; + IoHelper::DirectoryIterator it(permLostRoot, true, ioError); + + // Remove permission (after iterator is created) + bool result = IoHelper::setRights(subDir, false, false, false, ioError); + result &= ioError == IoErrorSuccess; + if (!result) { + IoHelper::setRights(subDir, true, true, true, ioError); + CPPUNIT_ASSERT(false /*setRights failed*/); + } + + DirectoryEntry entry; + bool endOfDirectory = false; + + result = it.next(entry, endOfDirectory, ioError); + result &= ioError == IoErrorSuccess; + + if (!result) { + IoHelper::setRights(subDir, true, true, true, ioError); + IoHelper::setRights(filePath, true, true, true, ioError); + } + + CPPUNIT_ASSERT(result /*result = it.next(entry, endOfDirectory, ioError);*/); + + result = it.next(entry, endOfDirectory, ioError); + result &= ioError == IoErrorSuccess; + + IoHelper::setRights(subDir, true, true, true, ioError); + IoHelper::setRights(filePath, true, true, true, ioError); + + CPPUNIT_ASSERT(result /*result = it.next(entry, endOfDirectory, ioError);*/); + CPPUNIT_ASSERT(endOfDirectory); + } +} + +} // namespace KDC diff --git a/test/libcommonserver/io/testchecksetgetrights.cpp b/test/libcommonserver/io/testchecksetgetrights.cpp index 7d5a35c90..9a4f2303d 100644 --- a/test/libcommonserver/io/testchecksetgetrights.cpp +++ b/test/libcommonserver/io/testchecksetgetrights.cpp @@ -35,11 +35,11 @@ static struct RightsSet { void TestIo::testCheckSetAndGetRights() { #ifdef _WIN32 - CPPUNIT_ASSERT(Utility::init()); // Initialize the utility library, needed to access/change the permissions on Windows + Utility::init(); // Initialize the utility library, needed to access/change the permissions on Windows #endif const TemporaryDirectory temporaryDirectory; - // Test if the rights are correctly set on a directory and if it can be retrieved successfully. + // Test if the rights are correctly set and get on a directory { const SyncPath path = temporaryDirectory.path / "changePerm"; @@ -82,12 +82,13 @@ void TestIo::testCheckSetAndGetRights() { } result = IoHelper::getRights(path, isReadable, isWritable, isExecutable, ioError); + result &= ioError == IoErrorSuccess; if (!result) { IoHelper::setRights(path, true, true, true, ioError); CPPUNIT_ASSERT(false /* Failed to get base rights */); } - if (!(ioError == IoErrorSuccess && isReadable == rightsSet.read && isWritable == rightsSet.write && + if (!(isReadable == rightsSet.read && isWritable == rightsSet.write && isExecutable == rightsSet.execute)) { IoHelper::setRights(path, true, true, true, ioError); CPPUNIT_ASSERT(false /* Set base rights mismatch with get base rights */); @@ -102,12 +103,13 @@ void TestIo::testCheckSetAndGetRights() { } result = IoHelper::getRights(path, isReadable, isWritable, isExecutable, ioError); + result &= ioError == IoErrorSuccess; if (!result) { IoHelper::setRights(path, true, true, true, ioError); CPPUNIT_ASSERT(false /* Failed to get target rights */); } - if (!(ioError == IoErrorSuccess && isReadable == rightsSet.read && isWritable == rightsSet.write && + if (!(isReadable == rightsSet.read && isWritable == rightsSet.write && isExecutable == rightsSet.execute)) { IoHelper::setRights(path, true, true, true, ioError); CPPUNIT_ASSERT(false /* Set target rights mismatch with get target rights */); @@ -148,12 +150,12 @@ void TestIo::testCheckSetAndGetRights() { } result = IoHelper::getRights(filepath, isReadable, isWritable, isExecutable, ioError); + result &= ioError == IoErrorSuccess; if (!result) { IoHelper::setRights(filepath, true, true, true, ioError); CPPUNIT_ASSERT(false /* Failed to get base rights */); } - - if (!(ioError == IoErrorSuccess && isReadable == rightsSet.read && isWritable == rightsSet.write && + if (!(isReadable == rightsSet.read && isWritable == rightsSet.write && isExecutable == rightsSet.execute)) { IoHelper::setRights(filepath, true, true, true, ioError); CPPUNIT_ASSERT(false /* Set base rights mismatch with get base rights */); @@ -166,14 +168,14 @@ void TestIo::testCheckSetAndGetRights() { IoHelper::setRights(filepath, true, true, true, ioError); CPPUNIT_ASSERT(false /* Failed to set target rights */); } - result = IoHelper::getRights(filepath, isReadable, isWritable, isExecutable, ioError); + result &= ioError == IoErrorSuccess; if (!result) { IoHelper::setRights(filepath, true, true, true, ioError); CPPUNIT_ASSERT(false /* Failed to get target rights */); } - if (!(ioError == IoErrorSuccess && isReadable == rightsSet.read && isWritable == rightsSet.write && + if (!(isReadable == rightsSet.read && isWritable == rightsSet.write && isExecutable == rightsSet.execute)) { IoHelper::setRights(filepath, true, true, true, ioError); CPPUNIT_ASSERT(false /* Set target rights mismatch with get target rights */); @@ -183,6 +185,7 @@ void TestIo::testCheckSetAndGetRights() { // Restore the rights IoHelper::setRights(filepath, true, true, true, ioError); + #ifdef _WIN32 CPPUNIT_ASSERT_EQUAL(0, IoHelper::_getAndSetRightsMethod); // Check that no error occurred with the wndows API #endif @@ -215,7 +218,6 @@ void TestIo::testCheckSetAndGetRights() { bool result = IoHelper::setRights(path, true, true, true, ioError); result = IoHelper::setRights(subFolderPath, true, true, true, ioError); result = IoHelper::setRights(subFilePath, true, true, true, ioError); - CPPUNIT_ASSERT(IoHelper::getRights(path, isReadable, isWritable, isExecutable, ioError)); CPPUNIT_ASSERT(ioError == IoErrorSuccess && isReadable && isWritable && isExecutable); diff --git a/test/libcommonserver/io/testgetfilestat.cpp b/test/libcommonserver/io/testgetfilestat.cpp index 95064a821..b6712f5b2 100644 --- a/test/libcommonserver/io/testgetfilestat.cpp +++ b/test/libcommonserver/io/testgetfilestat.cpp @@ -254,14 +254,14 @@ void TestIo::testGetFileStat() { bool exists = false; IoError ioError = IoErrorUnknown; #ifdef _WIN32 - CPPUNIT_ASSERT(!_testObj->getFileStat(path, &fileStat, exists, ioError)); + CPPUNIT_ASSERT(_testObj->getFileStat(path, &fileStat, exists, ioError)); CPPUNIT_ASSERT(!exists); CPPUNIT_ASSERT(!fileStat.isHidden); CPPUNIT_ASSERT(fileStat.size == 0u); CPPUNIT_ASSERT(fileStat.type == NodeTypeUnknown); CPPUNIT_ASSERT(fileStat.modtime == 0u); CPPUNIT_ASSERT(fileStat.modtime == fileStat.creationTime); - CPPUNIT_ASSERT(ioError == IoErrorUnknown); + CPPUNIT_ASSERT(ioError == IoErrorNoSuchFileOrDirectory); #else CPPUNIT_ASSERT(_testObj->getFileStat(path, &fileStat, exists, ioError)); CPPUNIT_ASSERT(exists); diff --git a/test/libcommonserver/io/testgetitemtype.cpp b/test/libcommonserver/io/testgetitemtype.cpp index 053e028a8..4786bdab7 100644 --- a/test/libcommonserver/io/testgetitemtype.cpp +++ b/test/libcommonserver/io/testgetitemtype.cpp @@ -207,8 +207,8 @@ void TestIo::testGetItemTypeSimpleCases() { #ifdef _WIN32 ItemType itemType; - CPPUNIT_ASSERT(!_testObj->getItemType(path, itemType)); - CPPUNIT_ASSERT(itemType.ioError == IoErrorUnknown); + CPPUNIT_ASSERT(_testObj->getItemType(path, itemType)); // Invalid name is considered as IoErrorNoSuchFileOrDirectory (expected error) + CPPUNIT_ASSERT(itemType.ioError == IoErrorNoSuchFileOrDirectory); CPPUNIT_ASSERT(itemType.nodeType == NodeTypeUnknown); CPPUNIT_ASSERT(itemType.linkType == LinkTypeNone); CPPUNIT_ASSERT(itemType.targetType == NodeTypeUnknown); diff --git a/test/libcommonserver/io/testio.h b/test/libcommonserver/io/testio.h index 93e3846bd..15bd06d28 100644 --- a/test/libcommonserver/io/testio.h +++ b/test/libcommonserver/io/testio.h @@ -61,6 +61,8 @@ class TestIo : public CppUnit::TestFixture { // CPPUNIT_TEST(testIsFileAccessible); // Temporary disabled: Infinite loop on Linux CI CPPUNIT_TEST(testFileChanged); CPPUNIT_TEST(testCheckIfIsHiddenFile); + CPPUNIT_TEST(testCheckSetAndGetRights); + CPPUNIT_TEST(testCheckDirectoryIterator); #if defined(__APPLE__) || defined(_WIN32) CPPUNIT_TEST(testGetXAttrValue); CPPUNIT_TEST(testSetXAttrValue); @@ -87,6 +89,7 @@ class TestIo : public CppUnit::TestFixture { void testTempDirectoryPath(void); void testLogDirectoryPath(void); void testGetNodeId(void); + void testCheckDirectoryIterator(void); void testCheckIfPathExists(void); void testCheckIfIsDirectory(void); void testCreateDirectory(void); @@ -121,6 +124,14 @@ class TestIo : public CppUnit::TestFixture { void testCheckIfPathExistsWithSameNodeIdSimpleCases(void); void testCheckIfPathExistsWithSameNodeIdAllBranches(void); + void testCheckDirectoryIteratorNonExistingPath(void); + void testCheckDirectoryIteratorExistingPath(void); + void testCheckDirectoryIteratotNextAfterEndOfDir(void); + void testCheckDirectoryIteratorPermission(void); + void testCheckDirectoryRecursive(void); + void testCheckDirectoryIteratorUnexpectedDelete(void); + void testCheckDirectoryPermissionLost(void); + private: IoHelperTests *_testObj; const SyncPath _localTestDirPath; diff --git a/test/libparms/db/testparmsdb.cpp b/test/libparms/db/testparmsdb.cpp index c5f076ee3..a0810589d 100644 --- a/test/libparms/db/testparmsdb.cpp +++ b/test/libparms/db/testparmsdb.cpp @@ -301,15 +301,26 @@ void TestParmsDb::testExclusionTemplate() { void TestParmsDb::testAppState(void) { bool found = true; std::string value; - CPPUNIT_ASSERT(ParmsDb::instance()->selectAppState(AppStateKey::Test, value, found) && found); - CPPUNIT_ASSERT_EQUAL(std::string("Test"), value); - CPPUNIT_ASSERT(ParmsDb::instance()->updateAppState(AppStateKey::Test, "value 1", found)); - CPPUNIT_ASSERT(ParmsDb::instance()->selectAppState(AppStateKey::Test, value, found) && found); - CPPUNIT_ASSERT_EQUAL(std::string("value 1"), value); + CPPUNIT_ASSERT( + ParmsDb::instance()->updateAppState(AppStateKey::Unknown, "value 2", found)); // Test for unknown key (not in db) + CPPUNIT_ASSERT(!found); - CPPUNIT_ASSERT(ParmsDb::instance()->updateAppState(static_cast(9548215525211611), "value 2", found)); + CPPUNIT_ASSERT(ParmsDb::instance()->selectAppState(AppStateKey::Unknown, value, found)); CPPUNIT_ASSERT(!found); + + int i = 0; + do { + AppStateKey key = static_cast(i); // Test for all known keys + if (key == AppStateKey::Unknown) { + break; + } + CPPUNIT_ASSERT(ParmsDb::instance()->selectAppState(key, value, found) && found); + CPPUNIT_ASSERT(ParmsDb::instance()->updateAppState(key, "value 1", found)); + CPPUNIT_ASSERT(ParmsDb::instance()->selectAppState(key, value, found) && found); + CPPUNIT_ASSERT_EQUAL(std::string("value 1"), value); + i++; + } while (true); } #ifdef __APPLE__