Fix: Startup-Dedup ersetzt keine gute kanonische Datei mehr durch ein kleineres Duplikat
Beim Start gleicht das Tool Duplikat-benannte Dateien ("Name (1).ext") gegen die
kanonische Datei ("Name.ext") ab. Im Zweig "Duplikat rangiert hoeher und das
kanonische Item ist nicht 'completed'" wurde bisher bedingungslos
fs.rmSync(kanonisch) + fs.renameSync(Duplikat → kanonisch) ausgefuehrt — ohne die
echten Dateigroessen zu vergleichen. Der Rang mischt den persistierten STATUS mit
der Disk-Existenz; nach einem Absturz kann der Status von der Realitaet abweichen.
War die kanonische Datei auf der Platte tatsaechlich vollstaendig/gut, ihr Item
aber z.B. "failed"/"queued", waehrend das Duplikat-Item ein veraltetes
"completed" ueber einer kleineren/partiellen Datei trug, wurde die gute
kanonische Datei geloescht und durch das schlechtere Duplikat ersetzt —
unwiederbringlicher Verlust der besseren Datei. Zusatzrisiko: rmSync erfolgreich,
renameSync wirft EXDEV → kanonisch weg, nichts installiert.
Fix:
- Size-Guard: Ist die kanonische Datei >= dem Duplikat, wird sie behalten und das
(kleinere/gleich grosse) Duplikat verworfen. Eine kleinere Datei kann eine
groessere nie ueberschreiben.
- Sichere Reihenfolge fuer den echten Austausch (kanonisch kleiner): kanonisch →
".dedupbak" umbenennen, dann Duplikat → kanonisch; bei Fehler (EXDEV o.ae.)
wird die kanonische Datei aus dem Backup wiederhergestellt. Kein Loss-Fenster
mehr. Erst nach Erfolg wird ".dedupbak" entfernt.
Test: kanonisch 1024 B (gut) + Duplikat 256 B (partiell), primaeres Item "failed"
(Duplikat rangiert hoeher) → die kanonische Datei bleibt mit 1024 B erhalten.
Ohne den Size-Guard wird sie durch die 256-B-Datei ersetzt (rot bewiesen). Volle
download-manager-Suite (168) gruen, tsc unveraendert 6.
This commit is contained in:
@@ -6392,19 +6392,38 @@ export class DownloadManager extends EventEmitter {
|
||||
} catch {
|
||||
}
|
||||
} else if (duplicateExists && canonicalExists && !primaryWins && primaryItem.status !== "completed") {
|
||||
try {
|
||||
fs.rmSync(canonicalPath, { force: true });
|
||||
fs.renameSync(duplicateTargetPath, canonicalPath);
|
||||
canonicalExists = true;
|
||||
this.logVerifiedRenameSync("startup-dedup (Austausch)", duplicateTargetPath, canonicalPath);
|
||||
logger.info(`startupDuplicateMerge: ersetze verwaisten Originalpfad ${canonicalBaseName} durch ${path.basename(duplicateTargetPath)}`);
|
||||
} catch (err) {
|
||||
logDesktopRename("ERROR", "startup-dedup (Austausch): Rename fehlgeschlagen", {
|
||||
source: path.basename(duplicateTargetPath),
|
||||
target: canonicalBaseName,
|
||||
error: compactErrorText(err)
|
||||
});
|
||||
logger.warn(`startupDuplicateMerge: Austausch fehlgeschlagen ${canonicalPath}: ${compactErrorText(err)}`);
|
||||
let canonicalSize = -1;
|
||||
let duplicateSize = -1;
|
||||
try { canonicalSize = fs.statSync(canonicalPath).size; } catch { }
|
||||
try { duplicateSize = fs.statSync(duplicateTargetPath).size; } catch { }
|
||||
if (canonicalSize >= 0 && duplicateSize >= 0 && canonicalSize >= duplicateSize) {
|
||||
try {
|
||||
fs.rmSync(duplicateTargetPath, { force: true });
|
||||
} catch {
|
||||
}
|
||||
logger.info(`startupDuplicateMerge: kanonische Datei behalten (${canonicalSize}B >= Duplikat ${duplicateSize}B), Duplikat verworfen: ${canonicalBaseName}`);
|
||||
} else {
|
||||
const dedupBackupPath = `${canonicalPath}.dedupbak`;
|
||||
try {
|
||||
fs.renameSync(canonicalPath, dedupBackupPath);
|
||||
try {
|
||||
fs.renameSync(duplicateTargetPath, canonicalPath);
|
||||
try { fs.rmSync(dedupBackupPath, { force: true }); } catch { }
|
||||
canonicalExists = true;
|
||||
this.logVerifiedRenameSync("startup-dedup (Austausch)", duplicateTargetPath, canonicalPath);
|
||||
logger.info(`startupDuplicateMerge: ersetze verwaisten Originalpfad ${canonicalBaseName} durch ${path.basename(duplicateTargetPath)}`);
|
||||
} catch (swapErr) {
|
||||
try { fs.renameSync(dedupBackupPath, canonicalPath); } catch { }
|
||||
throw swapErr;
|
||||
}
|
||||
} catch (err) {
|
||||
logDesktopRename("ERROR", "startup-dedup (Austausch): Rename fehlgeschlagen", {
|
||||
source: path.basename(duplicateTargetPath),
|
||||
target: canonicalBaseName,
|
||||
error: compactErrorText(err)
|
||||
});
|
||||
logger.warn(`startupDuplicateMerge: Austausch fehlgeschlagen ${canonicalPath}: ${compactErrorText(err)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user