summaryrefslogtreecommitdiffstats
path: root/recipes/ostree/ostree/Create-firmware-convenience-symlinks.patch
blob: 960367c6c64636404639816159b23b1b15ca5457 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
From 310ddd84dc353d93a2cc118725b459dba643cf0b Mon Sep 17 00:00:00 2001
From: Gatis Paeglis <gatis.paeglis@theqtcompany.com>
Date: Thu, 21 Apr 2016 16:54:05 +0200
Subject: [PATCH 3/3] Create firmware convenience symlinks.

Later this could be moved into utils or a similar
location, if other boot loader backends will need
this functionality.
---
 src/libostree/ostree-bootloader-uboot.c | 97 ++++++++++++++++++++++++++++++++-
 1 file changed, 96 insertions(+), 1 deletion(-)

diff --git a/src/libostree/ostree-bootloader-uboot.c b/src/libostree/ostree-bootloader-uboot.c
index 9bcde9c..be5e8c5 100644
--- a/src/libostree/ostree-bootloader-uboot.c
+++ b/src/libostree/ostree-bootloader-uboot.c
@@ -66,6 +66,100 @@ _ostree_bootloader_uboot_get_name (OstreeBootloader *bootloader)
   return "U-Boot";
 }
 
+/* It is common for firmware to search / on the boot partition for additional
+ * files that are required for booting. It can be difficult to change this search
+ * logic if it is hardcoded somewhere low in the stack or is in a read-only memory.
+ * This issue can be solved from the OS installer, by creating a symlink in the
+ * following way:
+ *
+ *    cd sysroot/boot
+ *    ln -s loader/second-stage-bootloader second-stage-bootloader
+ *
+ * This function will make sure that loader/second-stage-bootloader points to the
+ * correct target file version. This function does nothing if boot/ does not contain
+ * symlink files pointing into loader/.
+ */
+static gboolean
+create_firmware_convenience_symlinks (OstreeBootloaderUboot     *self,
+                                      char                  *bootcsum_dir,
+                                      int                    bootversion,
+                                      GCancellable          *cancellable,
+                                      GError               **error)
+{
+  gboolean ret = FALSE;
+  glnx_fd_close int loader_dfd = -1;
+  glnx_fd_close int boot_dfd = -1;
+  g_autofree char *loader_dir = NULL;
+  g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
+
+  loader_dir = g_strdup_printf ("boot/loader.%d/", bootversion);
+  if (!glnx_opendirat (self->sysroot->sysroot_fd, loader_dir, FALSE, &loader_dfd, error))
+    goto out;
+  if (!glnx_opendirat (self->sysroot->sysroot_fd, "boot", FALSE, &boot_dfd, error))
+    goto out;
+  if (!glnx_dirfd_iterator_init_take_fd (dup (boot_dfd), &dfd_iter, error))
+    goto out;
+
+  while (TRUE) {
+    struct dirent *dent;
+    struct stat stbuf;
+
+    if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
+      goto out;
+    if (dent == NULL)
+      break;
+
+    if (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
+      {
+        if (errno == ENOENT)
+          continue;
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "fstatat");
+        goto out;
+      }
+
+    if (S_ISLNK(stbuf.st_mode))
+      {
+        char path_buffer[PATH_MAX];
+        g_autofree char *symlink_target = NULL;
+        symlink_target = glnx_readlinkat_malloc (boot_dfd, dent->d_name, cancellable, error);
+
+        if (g_str_has_prefix (symlink_target, "loader/"))
+          {
+            if (g_strcmp0 (dent->d_name, "uEnv.txt") == 0)
+              continue;
+
+            snprintf (path_buffer, sizeof(path_buffer), "%s/%s", bootcsum_dir, dent->d_name);
+            if (faccessat (boot_dfd, path_buffer + 1, F_OK, AT_SYMLINK_NOFOLLOW) == -1)
+              {
+                /* This bootcsum dir does not contain the final target, do nothing. */
+                if (errno == ENOENT)
+                  continue;
+                g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "faccessat");
+                goto out;
+              }
+
+            /* In case 'ostree admin cleanup' was not run after an interrupted deployment */
+            if (unlinkat (loader_dfd, dent->d_name, 0) == -1 && errno != ENOENT)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "unlinkat");
+              goto out;
+            }
+            /* Complete the link chain to the current boot file version */
+            snprintf (path_buffer, sizeof(path_buffer), "..%s/%s", bootcsum_dir, dent->d_name);
+            if (symlinkat (path_buffer, loader_dfd, dent->d_name) == -1)
+              {
+                g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "symlinkat");
+                goto out;
+              }
+          }
+      }
+  }
+
+  ret = TRUE;
+out:
+  return ret;
+}
+
 static gboolean
 create_config_from_boot_loader_entries (OstreeBootloaderUboot     *self,
                                         int                    bootversion,
@@ -130,7 +224,8 @@ create_config_from_boot_loader_entries (OstreeBootloaderUboot     *self,
         }
     }
 
-  ret = TRUE;
+  ret = create_firmware_convenience_symlinks (self, bootdir, bootversion, cancellable, error);
+
 out:
   return ret;
 }
-- 
2.7.4