]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
efi: cache OsIndications too
authorLennart Poettering <lennart@poettering.net>
Wed, 27 May 2020 14:24:33 +0000 (16:24 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 29 May 2020 13:41:43 +0000 (15:41 +0200)
src/shared/efi-loader.c

index 8d93f4903a8ef2fcd4d4073cb72d7d2f04bc3591..b6ad43b856ec2b1f3b61b48991728241ec922dc6 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <stdlib.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -11,6 +12,7 @@
 #include "io-util.h"
 #include "parse-util.h"
 #include "sort-util.h"
+#include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "utf8.h"
@@ -100,31 +102,58 @@ not_supported:
         return -EOPNOTSUPP;
 }
 
-static int get_os_indications(uint64_t *os_indication) {
+static int get_os_indications(uint64_t *ret) {
+        static struct stat cache_stat = {};
         _cleanup_free_ void *v = NULL;
+        _cleanup_free_ char *fn = NULL;
+        static uint64_t cache;
+        struct stat new_stat;
         size_t s;
         int r;
 
+        assert(ret);
+
         /* Let's verify general support first */
         r = efi_reboot_to_firmware_supported();
         if (r < 0)
                 return r;
 
+        fn = efi_variable_path(EFI_VENDOR_GLOBAL, "OsIndications");
+        if (!fn)
+                return -ENOMEM;
+
+        /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
+        if (stat(fn, &new_stat) < 0) {
+                if (errno != ENOENT)
+                        return -errno;
+
+                /* Doesn't exist? Then we can exit early (also see below) */
+                *ret = 0;
+                return 0;
+
+        } else if (stat_inode_unmodified(&new_stat, &cache_stat)) {
+                /* inode didn't change, we can return the cached value */
+                *ret = cache;
+                return 0;
+        }
+
         r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
         if (r == -ENOENT) {
                 /* Some firmware implementations that do support OsIndications and report that with
-                 * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's pretend it's 0
-                 * then, to hide this implementation detail. Note that this call will return -ENOENT then only if the
-                 * support for OsIndications is missing entirely, as determined by efi_reboot_to_firmware_supported()
-                 * above. */
-                *os_indication = 0;
+                 * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's
+                 * pretend it's 0 then, to hide this implementation detail. Note that this call will return
+                 * -ENOENT then only if the support for OsIndications is missing entirely, as determined by
+                 * efi_reboot_to_firmware_supported() above. */
+                *ret = 0;
                 return 0;
-        } else if (r < 0)
+        }
+        if (r < 0)
                 return r;
         if (s != sizeof(uint64_t))
                 return -EINVAL;
 
-        *os_indication = *(uint64_t *)v;
+        cache_stat = new_stat;
+        *ret = cache = *(uint64_t *)v;
         return 0;
 }