Skip to content

Commit

Permalink
Merge pull request #43 from Infomaniak/KDESKTOP-737-Add-IoHelper-func…
Browse files Browse the repository at this point in the history
…tionality

Kdesktop 737 add io helper functionality
  • Loading branch information
ClementKunz authored May 2, 2024
2 parents 5ac9553 + 1305afd commit 8410114
Show file tree
Hide file tree
Showing 10 changed files with 540 additions and 16 deletions.
3 changes: 3 additions & 0 deletions src/libcommon/utility/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool, int, int64_t, uint64_t, double, std::string, std::wstring> SigValueType;

Expand Down Expand Up @@ -344,6 +346,7 @@ typedef enum {
IoErrorIsADirectory,
IoErrorNoSuchFileOrDirectory,
IoErrorResultOutOfRange,
IoErrorInvalidDirectoryIterator,
IoErrorUnknown
} IoError;

Expand Down
113 changes: 113 additions & 0 deletions src/libcommonserver/io/iohelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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 {
Expand Down
66 changes: 65 additions & 1 deletion src/libcommonserver/io/iohelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(){};

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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<bool(const SyncPath &path, std::error_code &ec)> _isDirectory;
Expand Down
3 changes: 2 additions & 1 deletion src/libcommonserver/io/iohelper_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion test/libcommonserver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 8410114

Please sign in to comment.