diff --git a/core/lib/Drupal/Component/FileSystem/FileSystem.php b/core/lib/Drupal/Component/FileSystem/FileSystem.php
index 7a1c551f24..2930e24b9e 100644
--- a/core/lib/Drupal/Component/FileSystem/FileSystem.php
+++ b/core/lib/Drupal/Component/FileSystem/FileSystem.php
@@ -23,7 +23,7 @@ public static function getOsTemporaryDirectory() {
     }
 
     // Operating system specific dirs.
-    if (substr(PHP_OS, 0, 3) == 'WIN') {
+    if (str_starts_with(PHP_OS, 'WIN')) {
       $directories[] = 'c:\\windows\\temp';
       $directories[] = 'c:\\winnt\\temp';
     }
@@ -42,5 +42,75 @@ public static function getOsTemporaryDirectory() {
     }
     return FALSE;
   }
+  /**
+   * Renames a file or directory.
+   *
+   * @param string $old_name
+   *   The old name. The wrapper used in old name must match the wrapper used in
+   *   newname.
+   * @param string $new_name
+   *   The new name.
+   * @param bool $over_write
+   *   Wether to overwrite the destination file if it already exists.
+   *
+   * @throws \Exception
+   */
+  public static function rename($old_name, $new_name, $over_write = FALSE) {
+    $exists = self::isReadable($new_name);
+    if (!$over_write && $exists) {
+      throw new \Exception(sprintf('Cannot rename because the target "%s" already exists.', $new_name), 0, NULL, $new_name);
+    }
+    if ($exists && !self::isWritable($new_name)) {
+      chmod($new_name, 0755);
+    }
+    return rename($old_name, $new_name);
+  }
+
+  /**
+   * Tells whether a file exists and is readable.
+   *
+   * @param string $file_name
+   *   Path to the file.
+   *
+   * @return bool
+   *
+   * @throws \Exception
+   *   When windows path is longer than 258 characters.
+   */
+  private static function isReadable($file_name) {
+    if (self::isWindows() && strlen($file_name) > 258) {
+      throw new \Exception('Could not check if file is readable because path length exceeds 258 characters.', 0, NULL, $filename);
+    }
+
+    return is_readable($file_name);
+  }
+
+  /**
+   * Check if this is a Windows system.
+   *
+   * @return bool
+   */
+  public static function isWindows() {
+    return '\\' === DIRECTORY_SEPARATOR;
+  }
+
+  /**
+   * Tells whether a file exists and is writable.
+   *
+   * @param string $file_name
+   *   Path to the file.
+   *
+   * @return bool
+   *
+   * @throws \Exception
+   *   When windows path is longer than 258 characters.
+   */
+  private static function isWritable($file_name) {
+    if (self::isWindows() && strlen($file_name) > 258) {
+      throw new \Exception('Could not check if file is readable because path length exceeds 258 characters.', 0, NULL, $filename);
+    }
+
+    return is_writable($file_name);
+  }
 
 }
diff --git a/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php b/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php
index ff1949418f..ec9694d9fe 100644
--- a/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php
+++ b/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Component\PhpStorage;
 
+use Drupal\Component\FileSystem\FileSystem;
 use Drupal\Component\Utility\Crypt;
 
 /**
@@ -85,7 +86,7 @@ public function save($name, $data) {
     $directory = $this->getContainingDirectoryFullPath($name);
     $this->ensureDirectory($directory);
     $full_path = $this->getFullPath($name, $directory, $mtime);
-    $result = rename($temporary_path, $full_path);
+    $result = FileSystem::rename($temporary_path, $full_path, TRUE);
 
     // Finally reset the modification time of the directory to match the one of
     // the newly created file. In order to prevent the creation of a file if the
diff --git a/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php b/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php
index 89a7a7522e..89ffcb75df 100644
--- a/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php
+++ b/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php
@@ -58,18 +58,6 @@ public function __construct(CacheBackendInterface $cache, $twig_cache_prefix) {
     $this->templateCacheFilenamePrefix = $twig_cache_prefix;
   }
 
-  /**
-   * Gets the PHP code storage object to use for the compiled Twig files.
-   *
-   * @return \Drupal\Component\PhpStorage\PhpStorageInterface
-   */
-  protected function storage() {
-    if (!isset($this->storage)) {
-      $this->storage = PhpStorageFactory::get('twig');
-    }
-    return $this->storage;
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -105,14 +93,27 @@ public function load(string $key): void {
     $this->storage()->load($key);
   }
 
+  /**
+   * Gets the PHP code storage object to use for the compiled Twig files.
+   *
+   * @return \Drupal\Component\PhpStorage\PhpStorageInterface
+   */
+  protected function storage() {
+    if (!isset($this->storage)) {
+      $this->storage = PhpStorageFactory::get('twig');
+    }
+    return $this->storage;
+  }
+
   /**
    * {@inheritdoc}
    */
   public function write(string $key, string $content): void {
-    $this->storage()->save($key, $content);
-    // Save the last mtime.
-    $cid = 'twig:' . $key;
-    $this->cache->set($cid, REQUEST_TIME);
+    if ($this->storage()->save($key, $content)) {
+      // Save the last mtime.
+      $cid = 'twig:' . $key;
+      $this->cache->set($cid, REQUEST_TIME);
+    }
   }
 
   /**
