$hash) { if (!preg_match('~^(libs/[A-Za-z0-9]+\\.php|Batterie/module\\.php|Manager/module\\.php)$~D', $name) || is_link($target . '/' . $name) || hash_file('sha256', $target . '/' . $name) !== $hash) { throw new RuntimeException('Abhaengigkeit wurde parallel geaendert: ' . $name); } } $before = []; $source = []; $already = true; foreach ($allowed as $name) { $path = $target . '/' . $name; $candidate = $stage . '/source/' . $name; if (is_link($path) || is_link($candidate) || realpath(dirname($path)) !== $target . '/' . dirname($name)) { throw new RuntimeException('Unerwarteter Dateipfad.'); } $source[$name] = (string) file_get_contents($candidate); if (hash('sha256', $source[$name]) !== $manifest['files'][$name]['after']) { throw new RuntimeException('Paketpruefsumme geaendert: ' . $name); } token_get_all($source[$name], TOKEN_PARSE); $before[$name] = is_file($path) ? file_get_contents($path) : null; $hash = $before[$name] === null ? null : hash('sha256', $before[$name]); if (!in_array($hash, [$manifest['files'][$name]['before'], $manifest['files'][$name]['after']], true)) { throw new RuntimeException('Paralleler Modulstand; nichts ueberschrieben: ' . $name); } if ($hash !== $manifest['files'][$name]['after']) { $already = false; } } if ($already) { return ['status' => 'already_installed', 'backup' => null, 'filesChanged' => []]; } $backup = $stage . '/backups/' . gmdate('Ymd\\THis\\Z') . '-' . bin2hex(random_bytes(4)); if (!mkdir($backup, 0700, true)) { throw new RuntimeException('Sicherung fehlgeschlagen.'); } foreach ($allowed as $name) { if ($before[$name] !== null) { $destination = $backup . '/' . $name; if (!is_dir(dirname($destination))) { mkdir(dirname($destination), 0700, true); } v4ConfirmedWrite($destination, $before[$name], 0600); } } v4ConfirmedWrite( $backup . '/MANIFEST.json', json_encode($manifest, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT) . "\n", 0600 ); $written = []; try { foreach ($allowed as $name) { if ((is_file($target . '/' . $name) ? file_get_contents($target . '/' . $name) : null) !== $before[$name]) { throw new RuntimeException('Parallele Aenderung waehrend Installation.'); } if ($before[$name] === $source[$name]) { continue; } v4ConfirmedWrite($target . '/' . $name, $source[$name]); $written[] = $name; } } catch (Throwable $error) { foreach (array_reverse($written) as $name) { $path = $target . '/' . $name; if (!is_link($path) && is_file($path) && hash_file('sha256', $path) === $manifest['files'][$name]['after']) { if ($before[$name] === null) { unlink($path); } else { v4ConfirmedWrite($path, $before[$name]); } } } throw $error; } return ['status' => 'installed', 'backup' => $backup, 'filesChanged' => $written]; }