| 233 | } |
| 234 | |
| 235 | size_t CleanupStaleImageCache(const std::string& images_dir) { |
| 236 | std::error_code ec; |
| 237 | if (!fs::exists(images_dir, ec) || !fs::is_directory(images_dir, ec)) { |
| 238 | return 0; |
| 239 | } |
| 240 | |
| 241 | size_t removed = 0; |
| 242 | for (const auto& entry : fs::directory_iterator(images_dir, ec)) { |
| 243 | if (ec) break; |
| 244 | if (!entry.is_directory()) continue; |
| 245 | |
| 246 | const auto dir = entry.path(); |
| 247 | bool is_stale = false; |
| 248 | |
| 249 | // Missing manifest: the downloader writes image.json only after all |
| 250 | // files have been renamed into place, so its absence means the |
| 251 | // previous download was killed mid-flight. |
| 252 | if (!fs::exists(dir / "image.json", ec)) { |
| 253 | is_stale = true; |
| 254 | } else { |
| 255 | // Manifest is present but orphan `.tmp` shards survived a crash. |
| 256 | for (const auto& child : fs::directory_iterator(dir, ec)) { |
| 257 | if (ec) break; |
| 258 | if (child.is_regular_file() && |
| 259 | child.path().extension() == ".tmp") { |
| 260 | is_stale = true; |
| 261 | break; |
| 262 | } |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | if (is_stale) { |
| 267 | std::error_code rm_ec; |
| 268 | fs::remove_all(dir, rm_ec); |
| 269 | if (!rm_ec) ++removed; |
| 270 | } |
| 271 | } |
| 272 | return removed; |
| 273 | } |
| 274 | |
| 275 | } // namespace image_source |