From 1d6189b6e6db7d0d6c126717b081901c5f552174 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 1 Oct 2026 13:24:11 +0200 Subject: fix(node): the reaper deletes only the .part files the node wrote MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Any *.part older than a day in a writable root was deleted — a browser's download in progress in a shared folder included. Only names carrying the node's tag (name.<8 hex>.part) are reaped now (F-28). Co-Authored-By: Claude Opus 5.5 --- packages/meshbay-node/src/meshbay_node/uploads.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'packages/meshbay-node/src/meshbay_node/uploads.py') diff --git a/packages/meshbay-node/src/meshbay_node/uploads.py b/packages/meshbay-node/src/meshbay_node/uploads.py index 92b854e..c485e39 100644 --- a/packages/meshbay-node/src/meshbay_node/uploads.py +++ b/packages/meshbay-node/src/meshbay_node/uploads.py @@ -24,6 +24,7 @@ build first. from __future__ import annotations +import re import secrets import time from collections.abc import Iterable @@ -45,6 +46,16 @@ def part_name(stored_name: str) -> str: """ return f"{stored_name}.{secrets.token_hex(4)}{PART_SUFFIX}" + +# What `part_name` writes, and the only thing the reaper deletes. A `.part` +# without the node's tag is somebody else's — a browser's download in progress in +# a shared folder, a copy the operator is making — and is never touched. +_OWN_PART = re.compile(r"\.[0-9a-f]{8}" + re.escape(PART_SUFFIX) + r"$") + + +def is_own_part(path: Path) -> bool: + return bool(_OWN_PART.search(path.name)) + # How long a `.part` with no upload behind it is kept before it is deleted. # # Generous on purpose. The cost of waiting is disk; the cost of being wrong is @@ -165,7 +176,7 @@ def orphaned_parts(candidates: Iterable[tuple[Path, float]], """ doomed: list[Path] = [] for path, mtime in candidates: - if path.suffix != PART_SUFFIX: + if not is_own_part(path): continue if path in live: continue -- cgit v1.2.3