#include "deletejob.h" #include "totalsizejob.h" #include "fileinfo_p.h" namespace Fm { bool DeleteJob::deleteFile(const FilePath& path, GFileInfoPtr inf) { ErrorAction act = ErrorAction::CONTINUE; while(!inf) { GErrorPtr err; inf = GFileInfoPtr{ g_file_query_info(path.gfile().get(), "standard::*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable().get(), &err), false }; if(err) { act = emitError(err, ErrorSeverity::SEVERE); if(act == ErrorAction::ABORT) { return false; } if(act != ErrorAction::RETRY) { break; } } } /* currently processed file. */ setCurrentFile(path); if(g_file_info_get_file_type(inf.get()) == G_FILE_TYPE_DIRECTORY) { // delete the content of the dir prior to deleting itself deleteDirContent(path, inf); } bool hasError = false; while(!isCancelled()) { GErrorPtr err; // try to delete the path directly if(g_file_delete(path.gfile().get(), cancellable().get(), &err)) { break; } if(err) { // FIXME: error handling /* if it's non-empty dir then descent into it then try again */ /* trash root gives G_IO_ERROR_PERMISSION_DENIED */ if(err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_NOT_EMPTY) { deleteDirContent(path, inf); } else if(err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_PERMISSION_DENIED) { /* special case for trash:/// */ /* FIXME: is there any better way to handle this? */ auto scheme = path.uriScheme(); if(g_strcmp0(scheme.get(), "trash") == 0) { break; } } act = emitError(err, ErrorSeverity::MODERATE); if(act != ErrorAction::RETRY) { hasError = true; break; } } } addFinishedAmount(g_file_info_get_size(inf.get()), 1); return !hasError; } bool DeleteJob::deleteDirContent(const FilePath& path, GFileInfoPtr inf) { #if 0 FmFolder* sub_folder; /* special handling for trash:/// */ if(!g_file_is_native(gf)) { char* scheme = g_file_get_uri_scheme(gf); if(g_strcmp0(scheme, "trash") == 0) { /* little trick: basename of trash root is /. */ char* basename = g_file_get_basename(gf); if(basename && basename[0] == G_DIR_SEPARATOR) { is_trash_root = true; } g_free(basename); } g_free(scheme); } #endif GErrorPtr err; GFileEnumeratorPtr enu { g_file_enumerate_children(path.gfile().get(), gfile_info_query_attribs, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable().get(), &err), false }; if(!enu) { emitError(err, ErrorSeverity::MODERATE); return false; } bool hasError = false; while(!isCancelled()) { inf = GFileInfoPtr{ g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false }; if(inf) { auto subPath = path.child(g_file_info_get_name(inf.get())); if(!deleteFile(subPath, inf)) { continue; } } else { if(err) { emitError(err, ErrorSeverity::MODERATE); /* ErrorAction::RETRY is not supported here */ hasError = true; } else { /* EOF */ } break; } } g_file_enumerator_close(enu.get(), nullptr, nullptr); return !hasError; } void DeleteJob::exec() { /* prepare the job, count total work needed with FmDeepCountJob */ TotalSizeJob totalSizeJob{paths_, TotalSizeJob::Flags::PREPARE_DELETE}; connect(&totalSizeJob, &TotalSizeJob::error, this, &DeleteJob::error); connect(this, &DeleteJob::cancelled, &totalSizeJob, &TotalSizeJob::cancel); totalSizeJob.run(); if(isCancelled()) { return; } setTotalAmount(totalSizeJob.totalSize(), totalSizeJob.fileCount()); Q_EMIT preparedToRun(); for(auto& path : paths_) { if(isCancelled()) { break; } deleteFile(path, GFileInfoPtr{nullptr}); } } } // namespace Fm