From 96457967e2ea8865ff92eb5bb1f7b9dd06ff0a8a Mon Sep 17 00:00:00 2001 From: Claas Flint Date: Fri, 2 Feb 2024 09:51:18 +0100 Subject: [PATCH 1/2] Fix and update firmware update process with error handling, logging, and file validation Details: - Download firmware from master branch: Retrieve firmware files directly from the master branch on GitHub for consistency and updates. - Implement robust error handling: Catch potential exceptions during download, extraction, and file operations to prevent unexpected failures and provide informative error messages. - Integrate logging: Use the Log facade to track progress, status, and potential errors for easier debugging and monitoring. - Add file validation: Verify the downloaded file size and content to ensure integrity and prevent issues with empty or invalid files. - Extract specific subfolders: Extract only firmware files within latest and feature-specific subfolders for targeted updates. - Improve code readability: Enhance code clarity with better variable naming, comments, and formatting for easier maintenance. --- app/Http/Livewire/Firmwares.php | 2 +- app/Models/Firmware.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Livewire/Firmwares.php b/app/Http/Livewire/Firmwares.php index 4087c9e..57436de 100644 --- a/app/Http/Livewire/Firmwares.php +++ b/app/Http/Livewire/Firmwares.php @@ -22,7 +22,7 @@ public function update() if (!file_exists($path)) mkdir($path); - $tmpfile = tempnam(sys_get_temp_dir(), "firmware-download"); + $tmpfile = tempnam(sys_get_temp_dir(), "firmware-download"); try { $client = new \GuzzleHttp\Client(); diff --git a/app/Models/Firmware.php b/app/Models/Firmware.php index bb00a0f..f2c6f9a 100644 --- a/app/Models/Firmware.php +++ b/app/Models/Firmware.php @@ -33,7 +33,7 @@ public static function allOfChannel($channel) public static function all() { $entries = []; - $channels = ["stable", "beta"]; + $channels = ["latest", "feature-specific"]; foreach ($channels as $channel) { From 8e89756b9daab2506827571b802c29816f5291a3 Mon Sep 17 00:00:00 2001 From: Claas Flint Date: Fri, 2 Feb 2024 09:54:12 +0100 Subject: [PATCH 2/2] Fix and update firmware update process with error handling, logging, and file validation Details: - Download firmware from master branch: Retrieve firmware files directly from the master branch on GitHub for consistency and updates. - Implement robust error handling: Catch potential exceptions during download, extraction, and file operations to prevent unexpected failures and provide informative error messages. - Integrate logging: Use the Log facade to track progress, status, and potential errors for easier debugging and monitoring. - Add file validation: Verify the downloaded file size and content to ensure integrity and prevent issues with empty or invalid files. - Extract specific subfolders: Extract only firmware files within latest and feature-specific subfolders for targeted updates. - Improve code readability: Enhance code clarity with better variable naming, comments, and formatting for easier maintenance. --- app/Http/Livewire/Firmwares.php | 112 +++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/app/Http/Livewire/Firmwares.php b/app/Http/Livewire/Firmwares.php index 57436de..bfe1831 100644 --- a/app/Http/Livewire/Firmwares.php +++ b/app/Http/Livewire/Firmwares.php @@ -5,6 +5,8 @@ use Livewire\Component; use App\Models\Firmware; use Illuminate\Support\Str; +use Illuminate\Support\Facades\Log; +use GuzzleHttp\Exception\GuzzleException; class Firmwares extends Component { @@ -18,54 +20,88 @@ public function render() public function update() { + session()->flash('message', 'Download started, please wait...'); + Log::info("Firmware update started"); $path = Firmware::basedir(); - if (!file_exists($path)) - mkdir($path); + if (!file_exists($path)) { + mkdir($path, 0755, true); + Log::info("Created directory: " . $path); + } - $tmpfile = tempnam(sys_get_temp_dir(), "firmware-download"); - try - { + try { $client = new \GuzzleHttp\Client(); - $r = $client->get('https://api.github.com/repos/raspberrypi/rpi-eeprom/zipball', ['sink' => $tmpfile]); - if ($r->getStatusCode() != 200) - throw new \Exception("Expected HTTP response code 200, but received ".$r->getStatusCode()." ".$r->getReasonPhrase() ); + $tmpfile = tempnam(sys_get_temp_dir(), "firmware-download"); + Log::info("Temporary file created: " . $tmpfile); + + $response = $client->get('https://github.com/raspberrypi/rpi-eeprom/archive/refs/heads/master.zip', [ + 'sink' => $tmpfile, + 'allow_redirects' => true + ]); + Log::info("HTTP request sent, response status: " . $response->getStatusCode()); + + if ($response->getStatusCode() != 200) { + throw new \Exception("Expected HTTP response code 200, but received " . $response->getStatusCode()); + } + + clearstatcache(true, $tmpfile); + $downloadedFileSize = filesize($tmpfile); + Log::info("Downloaded file size: " . $downloadedFileSize); + if ($downloadedFileSize == 0) { + throw new \Exception("Downloaded file is empty"); + } $zip = new \ZipArchive; - if (!$zip->open($tmpfile)) - throw new \Exception("Error opening .zip file"); - - // We only want rpi-eeprom-something/firmware/* of the .zip - $prefix = $zip->getNameIndex(0); - if (!$prefix) - throw new \Exception("Error listing .zip file"); - - $prefix .= "firmware/"; - - for ($i = 1; $i < $zip->numFiles; $i++) - { - $name = Str::of($zip->getNameIndex($i)); - if (!$name->startsWith($prefix) || $name->contains("../")) - continue; - - $nameWithoutPrefix = $name->substr(strlen($prefix)); - if (substr($nameWithoutPrefix, -1) == "/") - { - @mkdir($path."/".$nameWithoutPrefix, 0755, true); - } - else - { - @file_put_contents($path."/".$nameWithoutPrefix, $zip->getFromIndex($i)); + $openResult = $zip->open($tmpfile); + if ($openResult !== true) { + throw new \Exception("Error opening .zip file, ZipArchive open() returned: " . $openResult); + } + + if ($zip->numFiles == 0) { + throw new \Exception("Zip file contains no files"); + } + Log::info("file in .zip file " . $zip->numFiles); + + $allowedSubfolders = ['latest', 'feature-specific']; + $found = false; // Track if any matching file is found + + for ($i = 0; $i < $zip->numFiles; $i++) { + $name = $zip->getNameIndex($i); + if (preg_match('#rpi-eeprom-.+?/firmware-2711/(' . implode('|', $allowedSubfolders) . ')/(.*)#', $name, $matches)) { + $found = true; // Mark as found when a match is detected + $subfolder = $matches[1]; + $subfolderPath = $matches[2]; + + $destPath = $path . "/" . $subfolder . "/" . $subfolderPath; + + if (substr($name, -1) === "/") { + if (!is_dir($destPath)) { + mkdir($destPath, 0755, true); + Log::info("Created directory: " . $destPath); + } + } else { + $directoryPath = dirname($destPath); + if (!is_dir($directoryPath)) { + mkdir($directoryPath, 0755, true); + } + if (!is_dir($destPath)) { + file_put_contents($destPath, $zip->getFromIndex($i)); + Log::info("File extracted: " . $destPath); + } + } } } - $zip->close(); + if (!$found) { + throw new \Exception("Downloaded file has not the expected content."); + } + $zip->close(); session()->flash('message', 'Firmware updated'); + } catch (\Exception | GuzzleException $e) { + Log::error("Error: " . $e->getMessage()); + session()->flash('message', 'Error: ' . $e->getMessage()); + } finally { + @unlink($tmpfile); } - catch (\Exception $e) - { - session()->flash('message', 'Error: '.$e->getMessage() ); - } - @unlink($tmpfile); } }