diff --git a/src/libcommon/utility/types.h b/src/libcommon/utility/types.h index bfa3b589d..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; 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/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;