summaryrefslogtreecommitdiffstats
path: root/recipes/ostree/ostree/Create-firmware-convenience-symlinks.patch
blob: 656887dddbc4cd429e29202988183d9c91b9ac5f (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
From c4df63488b9e09a9aa69e32ea5c0671c9dc50c9d Mon Sep 17 00:00:00 2001
From: Gatis Paeglis <gatis.paeglis@qt.io>
Date: Wed, 24 Aug 2016 12:29:38 +0200
Subject: [PATCH] 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 | 93 ++++++++++++++++++++++++++++++++-
 1 file changed, 92 insertions(+), 1 deletion(-)

diff --git a/src/libostree/ostree-bootloader-uboot.c b/src/libostree/ostree-bootloader-uboot.c
index 22251da..26a3127 100644
--- a/src/libostree/ostree-bootloader-uboot.c
+++ b/src/libostree/ostree-bootloader-uboot.c
@@ -62,6 +62,97 @@ _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 by system builders by creating a convenience symlink:
+ *
+ *    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 the loader/ directory.
+ */
+static gboolean
+create_firmware_convenience_symlinks (OstreeBootloaderUboot *self,
+                                      char                  *bootcsum_dir,
+                                      int                    bootversion,
+                                      GCancellable          *cancellable,
+                                      GError               **error)
+{
+  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))
+    return FALSE;
+  if (!glnx_opendirat (self->sysroot->sysroot_fd, "boot", FALSE, &boot_dfd, error))
+    return FALSE;
+  if (!glnx_dirfd_iterator_init_take_fd (dup (boot_dfd), &dfd_iter, error))
+    return FALSE;
+
+  while (TRUE) {
+    struct dirent *dent;
+    struct stat stbuf;
+
+    if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
+      return FALSE;
+    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");
+        return FALSE;
+      }
+
+    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");
+                return FALSE;
+              }
+
+            /* Cleanup from stray symlinks. This can happend when the previous deployment was
+               interrupted and no cleanup routines were run before restaring the deployment. */
+            if (unlinkat (loader_dfd, dent->d_name, 0) == -1 && errno != ENOENT)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "unlinkat");
+              return FALSE;
+            }
+            /* 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");
+                return FALSE;
+              }
+          }
+      }
+  }
+
+  return TRUE;
+}
+
 static gboolean
 create_config_from_boot_loader_entries (OstreeBootloaderUboot     *self,
                                         int                    bootversion,
@@ -138,7 +229,7 @@ create_config_from_boot_loader_entries (OstreeBootloaderUboot     *self,
         }
     }
 
-  return TRUE;
+  return create_firmware_convenience_symlinks (self, bootdir, bootversion, cancellable, error);
 }
 
 static gboolean
-- 
2.7.4