Full restore: an empty listing never empties the index; read a failed pack once more; recognise and replace SFTP connections a Storage Box dropped quietly; version 0.9.11

This commit is contained in:
2026-10-19 21:53:05 +02:00
parent 0accd5a3f2
commit c37b26ea38
9 changed files with 324 additions and 28 deletions
+16 -5
View File
@@ -282,10 +282,19 @@ final class StorageBackend implements IBackend, IReadsAhead {
if ($connections === []) {
return null;
}
return (static function () use ($storages, $connections, $root, $abs, $spool): \Generator {
// in place of a connection that broke: a new storage, kept like the others
$connect = static function () use ($reconnect, &$storages): ?object {
$storage = $reconnect();
$sftp = SftpReadStream::connection($storage);
if ($sftp !== null) {
$storages[] = $storage;
}
return $sftp;
};
return (static function () use (&$storages, $connections, $root, $abs, $spool, $connect): \Generator {
// the storages hold their connections: kept until the fetcher is done
try {
yield from SftpRangeFetcher::fetch($connections, $root, $abs, $spool);
yield from SftpRangeFetcher::fetch($connections, $root, $abs, $spool, $connect);
} finally {
unset($storages);
}
@@ -478,8 +487,9 @@ final class StorageBackend implements IBackend, IReadsAhead {
$this->lastUsed = microtime(true);
}
/** After a failed request: ask the server (one round trip), as phpseclib may not know it is gone. */
private function reopenIfDead(): bool {
if ($this->reconnect === null || !$this->sftpReads || $this->alive(false) !== false) {
if ($this->reconnect === null || !$this->sftpReads || $this->alive(true) !== false) {
return false;
}
$this->reopen();
@@ -508,8 +518,9 @@ final class StorageBackend implements IBackend, IReadsAhead {
if (is_callable([$sftp, 'clearStatCache'])) {
$sftp->clearStatCache(); // or stat() answers from memory
}
$sftp->stat('.');
return (bool)$sftp->isConnected();
// A Storage Box drops a quiet connection without phpseclib noticing: it still says
// connected, and every request fails. So the answer counts, not what it says after.
return $sftp->stat('.') !== false && (bool)$sftp->isConnected();
} catch (\Throwable) {
return false;
}