67 lines
4.1 KiB
PHP
67 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/** File-only installation helpers. No module classes loaded from the staging tree. */
|
|
function v4FeedbackWrite(string $path, string $content, int $mode=0644): void
|
|
{
|
|
if (is_link($path)) throw new RuntimeException('Symlink wird nicht ersetzt.');
|
|
$temp=tempnam(dirname($path),'.v4-feedback-');
|
|
if ($temp===false) throw new RuntimeException('Temporaere Datei nicht verfuegbar.');
|
|
try {
|
|
if (file_put_contents($temp,$content,LOCK_EX)!==strlen($content) || !chmod($temp,$mode) || !rename($temp,$path)) throw new RuntimeException('Atomarer Dateitausch fehlgeschlagen.');
|
|
} finally { if(is_file($temp))unlink($temp); }
|
|
}
|
|
|
|
function v4FeedbackFiles(string $stage,string $target): array
|
|
{
|
|
$allowed=['libs/NetzfahrplanV4Rueckmeldung.php','libs/BatterieNetzfahrplanV4RueckmeldungTrait.php','libs/NetzfahrplanV4Regeltest.php',
|
|
'libs/BatterieNetzfahrplanV4TestTrait.php','libs/ManagerNetzfahrplanV4TestTrait.php','libs/ManagerNetzfahrplanV4EmpfangTrait.php','libs/BatterieRegler.php','Batterie/module.php','Manager/module.php'];
|
|
$m=json_decode((string)file_get_contents($stage.'/MANIFEST.json'),true,32,JSON_THROW_ON_ERROR);
|
|
if (($m['scope']??null)!=='corrected_feedback_trial_disabled' || count($m['files']??[])!==count($allowed) || array_diff(array_keys($m['files']),$allowed)) throw new RuntimeException('Unerwarteter Dateiumfang.');
|
|
if (realpath($target)!==$target || is_link($target)) throw new RuntimeException('Modulpfad unerwartet.');
|
|
foreach($m['dependencies'] as $name=>$hash) {
|
|
if (!preg_match('~^libs/[A-Za-z0-9]+\.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;
|
|
// Explicit dependency order. Both top-level modules are replaced only after all dependencies.
|
|
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])!==$m['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,[$m['files'][$name]['before'],$m['files'][$name]['after']],true)) throw new RuntimeException('Paralleler Modulstand; nichts ueberschrieben: '.$name);
|
|
if($hash!==$m['files'][$name]['after'])$already=false;
|
|
}
|
|
if($already)return ['status'=>'already_installed','backup'=>null];
|
|
$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) {
|
|
$dest=$backup.'/'.$name;if(!is_dir(dirname($dest)))mkdir(dirname($dest),0700,true);
|
|
v4FeedbackWrite($dest,$before[$name],0600);
|
|
}
|
|
}
|
|
v4FeedbackWrite($backup.'/MANIFEST.json',json_encode($m,JSON_THROW_ON_ERROR|JSON_PRETTY_PRINT),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;
|
|
v4FeedbackWrite($target.'/'.$name,$source[$name]);$written[]=$name;
|
|
}
|
|
} catch(Throwable $e) {
|
|
foreach(array_reverse($written)as$name) {
|
|
$p=$target.'/'.$name;
|
|
if(!is_link($p)&&is_file($p)&&hash_file('sha256',$p)===$m['files'][$name]['after']) {
|
|
if($before[$name]===null)unlink($p);else v4FeedbackWrite($p,$before[$name]);
|
|
}
|
|
}
|
|
throw $e;
|
|
}
|
|
return ['status'=>'installed','backup'=>$backup,'filesChanged'=>$written];
|
|
}
|