diff --git a/Userland/Applications/FileManager/DirectoryView.cpp b/Userland/Applications/FileManager/DirectoryView.cpp index 02c411b36d..2dbcd68b54 100644 --- a/Userland/Applications/FileManager/DirectoryView.cpp +++ b/Userland/Applications/FileManager/DirectoryView.cpp @@ -580,8 +580,8 @@ void DirectoryView::handle_drop(const GUI::ModelIndex& index, const GUI::DropEve if (url_to_copy.path() == new_path) continue; - if (!FileUtils::copy_file_or_directory(url_to_copy.path(), new_path)) { - auto error_message = String::formatted("Could not copy {} into {}.", url_to_copy.to_string(), new_path); + if (auto result = Core::File::copy_file_or_directory(new_path, url_to_copy.path()); result.is_error()) { + auto error_message = String::formatted("Could not copy {} into {}: {}", url_to_copy.to_string(), new_path, result.error().error_code); GUI::MessageBox::show(window(), error_message, "File Manager", GUI::MessageBox::Type::Error); } else { had_accepted_drop = true; diff --git a/Userland/Applications/FileManager/FileUtils.cpp b/Userland/Applications/FileManager/FileUtils.cpp index 76d2ee0079..c9639916db 100644 --- a/Userland/Applications/FileManager/FileUtils.cpp +++ b/Userland/Applications/FileManager/FileUtils.cpp @@ -26,13 +26,9 @@ #include "FileUtils.h" #include -#include -#include #include #include #include -#include -#include #include #include @@ -48,22 +44,22 @@ void delete_path(const String& path, GUI::Window* parent_window) GUI::MessageBox::Type::Error); } - if (S_ISDIR(st.st_mode)) { - String error_path; - int error = FileUtils::delete_directory(path, error_path); + bool is_directory = S_ISDIR(st.st_mode); + auto result = Core::File::remove(path, Core::File::RecursionMode::Allowed, false); - if (error) { + if (result.is_error()) { + auto& error = result.error(); + if (is_directory) { GUI::MessageBox::show(parent_window, - String::formatted("Failed to delete directory \"{}\": {}", error_path, strerror(error)), + String::formatted("Failed to delete directory \"{}\": {}", error.file, error.error_code), + "Delete failed", + GUI::MessageBox::Type::Error); + } else { + GUI::MessageBox::show(parent_window, + String::formatted("Failed to delete file \"{}\": {}", error.file, error.error_code), "Delete failed", GUI::MessageBox::Type::Error); } - } else if (unlink(path.characters()) < 0) { - int saved_errno = errno; - GUI::MessageBox::show(parent_window, - String::formatted("unlink(\"{}\") failed: {}", path, strerror(saved_errno)), - "Delete failed", - GUI::MessageBox::Type::Error); } } @@ -91,190 +87,4 @@ void delete_paths(const Vector& paths, bool should_confirm, GUI::Window* } } -int delete_directory(String directory, String& file_that_caused_error) -{ - Core::DirIterator iterator(directory, Core::DirIterator::SkipDots); - if (iterator.has_error()) { - file_that_caused_error = directory; - return -1; - } - - while (iterator.has_next()) { - auto file_to_delete = String::formatted("{}/{}", directory, iterator.next_path()); - struct stat st; - - if (lstat(file_to_delete.characters(), &st)) { - file_that_caused_error = file_to_delete; - return errno; - } - - if (S_ISDIR(st.st_mode)) { - if (delete_directory(file_to_delete, file_to_delete)) { - file_that_caused_error = file_to_delete; - return errno; - } - } else if (unlink(file_to_delete.characters())) { - file_that_caused_error = file_to_delete; - return errno; - } - } - - if (rmdir(directory.characters())) { - file_that_caused_error = directory; - return errno; - } - - return 0; -} - -bool copy_file_or_directory(const String& src_path, const String& dst_path) -{ - int duplicate_count = 0; - while (access(get_duplicate_name(dst_path, duplicate_count).characters(), F_OK) == 0) { - ++duplicate_count; - } - if (duplicate_count != 0) { - return copy_file_or_directory(src_path, get_duplicate_name(dst_path, duplicate_count)); - } - - auto source_or_error = Core::File::open(src_path, Core::IODevice::ReadOnly); - if (source_or_error.is_error()) - return false; - - auto& source = *source_or_error.value(); - - struct stat src_stat; - int rc = fstat(source.fd(), &src_stat); - if (rc < 0) - return false; - - if (source.is_directory()) - return copy_directory(src_path, dst_path, src_stat); - - return copy_file(dst_path, src_stat, source); -} - -bool copy_directory(const String& src_path, const String& dst_path, const struct stat& src_stat) -{ - int rc = mkdir(dst_path.characters(), 0755); - if (rc < 0) { - return false; - } - Core::DirIterator di(src_path, Core::DirIterator::SkipDots); - if (di.has_error()) { - return false; - } - while (di.has_next()) { - String filename = di.next_path(); - bool is_copied = copy_file_or_directory( - String::formatted("{}/{}", src_path, filename), - String::formatted("{}/{}", dst_path, filename)); - if (!is_copied) { - return false; - } - } - - auto my_umask = umask(0); - umask(my_umask); - rc = chmod(dst_path.characters(), src_stat.st_mode & ~my_umask); - if (rc < 0) { - return false; - } - return true; -} - -bool copy_file(const String& dst_path, const struct stat& src_stat, Core::File& source) -{ - int dst_fd = creat(dst_path.characters(), 0666); - if (dst_fd < 0) { - if (errno != EISDIR) { - return false; - } - auto dst_dir_path = String::formatted("{}/{}", dst_path, LexicalPath(source.filename()).basename()); - dst_fd = creat(dst_dir_path.characters(), 0666); - if (dst_fd < 0) { - return false; - } - } - - ScopeGuard close_fd_guard([dst_fd]() { close(dst_fd); }); - - if (src_stat.st_size > 0) { - if (ftruncate(dst_fd, src_stat.st_size) < 0) { - perror("cp: ftruncate"); - return false; - } - } - - for (;;) { - char buffer[32768]; - ssize_t nread = read(source.fd(), buffer, sizeof(buffer)); - if (nread < 0) { - return false; - } - if (nread == 0) - break; - ssize_t remaining_to_write = nread; - char* bufptr = buffer; - while (remaining_to_write) { - ssize_t nwritten = write(dst_fd, bufptr, remaining_to_write); - if (nwritten < 0) { - return false; - } - assert(nwritten > 0); - remaining_to_write -= nwritten; - bufptr += nwritten; - } - } - - auto my_umask = umask(0); - umask(my_umask); - int rc = fchmod(dst_fd, src_stat.st_mode & ~my_umask); - if (rc < 0) { - return false; - } - - return true; -} - -bool link_file(const String& src_path, const String& dst_path) -{ - int duplicate_count = 0; - while (access(get_duplicate_name(dst_path, duplicate_count).characters(), F_OK) == 0) { - ++duplicate_count; - } - if (duplicate_count != 0) { - return link_file(src_path, get_duplicate_name(dst_path, duplicate_count)); - } - int rc = symlink(src_path.characters(), dst_path.characters()); - if (rc < 0) { - return false; - } - - return true; -} - -String get_duplicate_name(const String& path, int duplicate_count) -{ - if (duplicate_count == 0) { - return path; - } - LexicalPath lexical_path(path); - StringBuilder duplicated_name; - duplicated_name.append('/'); - for (size_t i = 0; i < lexical_path.parts().size() - 1; ++i) { - duplicated_name.appendff("{}/", lexical_path.parts()[i]); - } - auto prev_duplicate_tag = String::formatted("({})", duplicate_count); - auto title = lexical_path.title(); - if (title.ends_with(prev_duplicate_tag)) { - // remove the previous duplicate tag "(n)" so we can add a new tag. - title = title.substring(0, title.length() - prev_duplicate_tag.length()); - } - duplicated_name.appendff("{} ({})", lexical_path.title(), duplicate_count); - if (!lexical_path.extension().is_empty()) { - duplicated_name.appendff(".{}", lexical_path.extension()); - } - return duplicated_name.build(); -} } diff --git a/Userland/Applications/FileManager/FileUtils.h b/Userland/Applications/FileManager/FileUtils.h index fcf6b483ac..314da67a70 100644 --- a/Userland/Applications/FileManager/FileUtils.h +++ b/Userland/Applications/FileManager/FileUtils.h @@ -40,11 +40,4 @@ enum class FileOperation { void delete_path(const String&, GUI::Window*); void delete_paths(const Vector&, bool should_confirm, GUI::Window*); -int delete_directory(String directory, String& file_that_caused_error); -bool copy_file_or_directory(const String& src_path, const String& dst_path); -String get_duplicate_name(const String& path, int duplicate_count); -bool copy_file(const String& dst_path, const struct stat& src_stat, Core::File&); -bool copy_directory(const String& src_path, const String& dst_path, const struct stat& src_stat); -bool link_file(const String& src_path, const String& dst_path); - } diff --git a/Userland/Applications/FileManager/main.cpp b/Userland/Applications/FileManager/main.cpp index 80819571f8..568ef53f94 100644 --- a/Userland/Applications/FileManager/main.cpp +++ b/Userland/Applications/FileManager/main.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -171,8 +172,8 @@ void do_paste(const String& target_directory, GUI::Window* window) } auto new_path = String::formatted("{}/{}", target_directory, url.basename()); - if (!FileUtils::copy_file_or_directory(url.path(), new_path)) { - auto error_message = String::formatted("Could not paste {}.", url.path()); + if (auto result = Core::File::copy_file_or_directory(new_path, url.path()); result.is_error()) { + auto error_message = String::formatted("Could not paste '{}': {}", url.path(), result.error().error_code); GUI::MessageBox::show(window, error_message, "File Manager", GUI::MessageBox::Type::Error); } else if (should_delete_src) { FileUtils::delete_path(url.path(), window); @@ -184,8 +185,8 @@ void do_create_link(const Vector& selected_file_paths, GUI::Window* wind { auto path = selected_file_paths.first(); auto destination = String::formatted("{}/{}", Core::StandardPaths::desktop_directory(), LexicalPath { path }.basename()); - if (!FileUtils::link_file(path, destination)) { - GUI::MessageBox::show(window, "Could not create desktop shortcut", "File Manager", + if (auto result = Core::File::link_file(destination, path); result.is_error()) { + GUI::MessageBox::show(window, String::formatted("Could not create desktop shortcut:\n{}", result.error()), "File Manager", GUI::MessageBox::Type::Error); } } @@ -984,8 +985,8 @@ int run_in_windowed_mode(RefPtr config, String initial_locatio if (url_to_copy.path() == new_path) continue; - if (!FileUtils::copy_file_or_directory(url_to_copy.path(), new_path)) { - auto error_message = String::formatted("Could not copy {} into {}.", url_to_copy.to_string(), new_path); + if (auto result = Core::File::copy_file_or_directory(url_to_copy.path(), new_path); result.is_error()) { + auto error_message = String::formatted("Could not copy {} into {}:\n {}", url_to_copy.to_string(), new_path, result.error().error_code); GUI::MessageBox::show(window, error_message, "File Manager", GUI::MessageBox::Type::Error); } else { had_accepted_copy = true;