| 1688 | } |
| 1689 | |
| 1690 | Status GcsFileSystem::DeleteRecursively(const string& dirname, |
| 1691 | int64* undeleted_files, |
| 1692 | int64* undeleted_dirs) { |
| 1693 | if (!undeleted_files || !undeleted_dirs) { |
| 1694 | return errors::Internal( |
| 1695 | "'undeleted_files' and 'undeleted_dirs' cannot be nullptr."); |
| 1696 | } |
| 1697 | *undeleted_files = 0; |
| 1698 | *undeleted_dirs = 0; |
| 1699 | if (!IsDirectory(dirname).ok()) { |
| 1700 | *undeleted_dirs = 1; |
| 1701 | return Status( |
| 1702 | error::NOT_FOUND, |
| 1703 | strings::StrCat(dirname, " doesn't exist or not a directory.")); |
| 1704 | } |
| 1705 | std::vector<string> all_objects; |
| 1706 | // Get all children in the directory recursively. |
| 1707 | TF_RETURN_IF_ERROR(GetChildrenBounded( |
| 1708 | dirname, UINT64_MAX, &all_objects, true /* recursively */, |
| 1709 | true /* include_self_directory_marker */)); |
| 1710 | for (const string& object : all_objects) { |
| 1711 | const string& full_path = JoinGcsPath(dirname, object); |
| 1712 | // Delete all objects including directory markers for subfolders. |
| 1713 | // Since DeleteRecursively returns OK if individual file deletions fail, |
| 1714 | // and therefore RetryingFileSystem won't pay attention to the failures, |
| 1715 | // we need to make sure these failures are properly retried. |
| 1716 | const auto& delete_file_status = RetryingUtils::DeleteWithRetries( |
| 1717 | [this, &full_path]() { return DeleteFile(full_path); }, retry_config_); |
| 1718 | if (!delete_file_status.ok()) { |
| 1719 | if (IsDirectory(full_path).ok()) { |
| 1720 | // The object is a directory marker. |
| 1721 | (*undeleted_dirs)++; |
| 1722 | } else { |
| 1723 | (*undeleted_files)++; |
| 1724 | } |
| 1725 | } |
| 1726 | } |
| 1727 | return Status::OK(); |
| 1728 | } |
| 1729 | |
| 1730 | // Flushes all caches for filesystem metadata and file contents. Useful for |
| 1731 | // reclaiming memory once filesystem operations are done (e.g. model is loaded), |