/src/ostree/src/libostree/ostree-bootloader-aboot.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2022 Eric Curtin <ericcurtin17@gmail.com> |
3 | | * |
4 | | * This program is free software: you can redistribute it and/or modify |
5 | | * it under the terms of the GNU Lesser General Public License as published |
6 | | * by the Free Software Foundation; either version 2 of the licence or (at |
7 | | * your option) any later version. |
8 | | * |
9 | | * This library is distributed in the hope that it will be useful, |
10 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | | * Lesser General Public License for more details. |
13 | | * |
14 | | * You should have received a copy of the GNU Lesser General |
15 | | * Public License along with this library. If not, see <https://www.gnu.org/licenses/>. |
16 | | */ |
17 | | |
18 | | #include "config.h" |
19 | | |
20 | | #include "ostree-bootloader-aboot.h" |
21 | | #include "ostree-deployment-private.h" |
22 | | #include "ostree-libarchive-private.h" |
23 | | #include "ostree-sysroot-private.h" |
24 | | #include "otutil.h" |
25 | | #include <sys/mount.h> |
26 | | #include <stdint.h> |
27 | | |
28 | | #include <string.h> |
29 | | |
30 | | /* This is specific to aboot and zipl today, but in the future we could also |
31 | | * use it for the grub2-mkconfig case. |
32 | | */ |
33 | | static const char aboot_requires_execute_path[] = "boot/ostree-bootloader-update.stamp"; |
34 | | |
35 | | struct _OstreeBootloaderAboot |
36 | | { |
37 | | GObject parent_instance; |
38 | | |
39 | | OstreeSysroot *sysroot; |
40 | | }; |
41 | | |
42 | | typedef GObjectClass OstreeBootloaderAbootClass; |
43 | | static void _ostree_bootloader_aboot_bootloader_iface_init (OstreeBootloaderInterface *iface); |
44 | 0 | G_DEFINE_TYPE_WITH_CODE (OstreeBootloaderAboot, _ostree_bootloader_aboot, G_TYPE_OBJECT, |
45 | 0 | G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BOOTLOADER, |
46 | 0 | _ostree_bootloader_aboot_bootloader_iface_init)); |
47 | 0 |
|
48 | 0 | static gboolean |
49 | 0 | _ostree_bootloader_aboot_query (OstreeBootloader *bootloader, gboolean *out_is_active, |
50 | 0 | GCancellable *cancellable, GError **error) |
51 | 0 | { |
52 | | /* We don't auto-detect this one; should be explicitly chosen right now. |
53 | | * see also https://github.com/coreos/coreos-assembler/pull/849 |
54 | | */ |
55 | 0 | *out_is_active = FALSE; |
56 | 0 | return TRUE; |
57 | 0 | } |
58 | | |
59 | | static const char * |
60 | | _ostree_bootloader_aboot_get_name (OstreeBootloader *bootloader) |
61 | 0 | { |
62 | 0 | return "aboot"; |
63 | 0 | } |
64 | | |
65 | | static gboolean |
66 | | _ostree_bootloader_aboot_write_config (OstreeBootloader *bootloader, int bootversion, |
67 | | GPtrArray *new_deployments, GCancellable *cancellable, |
68 | | GError **error) |
69 | 0 | { |
70 | 0 | OstreeBootloaderAboot *self = OSTREE_BOOTLOADER_ABOOT (bootloader); |
71 | | |
72 | | /* Write our stamp file */ |
73 | 0 | if (!glnx_file_replace_contents_at (self->sysroot->sysroot_fd, aboot_requires_execute_path, |
74 | 0 | (guint8 *)"", 0, GLNX_FILE_REPLACE_NODATASYNC, cancellable, |
75 | 0 | error)) |
76 | 0 | return FALSE; |
77 | | |
78 | 0 | return TRUE; |
79 | 0 | } |
80 | | |
81 | | static gboolean |
82 | | _ostree_aboot_get_bls_config (OstreeBootloaderAboot *self, int bootversion, gchar **aboot, |
83 | | gchar **abootcfg, gchar **version, gchar **vmlinuz, gchar **initramfs, |
84 | | gchar **options, GCancellable *cancellable, GError **error) |
85 | 0 | { |
86 | 0 | g_autoptr (GPtrArray) configs = NULL; |
87 | 0 | if (!_ostree_sysroot_read_boot_loader_configs (self->sysroot, bootversion, &configs, cancellable, |
88 | 0 | error)) |
89 | 0 | return glnx_prefix_error (error, "aboot: loading bls configs"); |
90 | | |
91 | 0 | if (!configs || configs->len == 0) |
92 | 0 | return glnx_throw (error, "aboot: no bls config"); |
93 | | |
94 | 0 | OstreeBootconfigParser *parser = (OstreeBootconfigParser *)g_ptr_array_index (configs, 0); |
95 | 0 | const gchar *val = NULL; |
96 | |
|
97 | 0 | val = ostree_bootconfig_parser_get (parser, "aboot"); |
98 | 0 | if (!val) |
99 | 0 | { |
100 | 0 | return glnx_throw (error, "aboot: no \"aboot\" key in bootloader config"); |
101 | 0 | } |
102 | 0 | *aboot = g_strdup (val); |
103 | |
|
104 | 0 | val = ostree_bootconfig_parser_get (parser, "abootcfg"); |
105 | 0 | if (!val) |
106 | 0 | { |
107 | 0 | return glnx_throw (error, "aboot: no \"abootcfg\" key in bootloader config"); |
108 | 0 | } |
109 | 0 | *abootcfg = g_strdup (val); |
110 | |
|
111 | 0 | val = ostree_bootconfig_parser_get (parser, "version"); |
112 | 0 | if (!val) |
113 | 0 | return glnx_throw (error, "aboot: no \"version\" key in bootloader config"); |
114 | 0 | *version = g_strdup (val); |
115 | |
|
116 | 0 | val = ostree_bootconfig_parser_get (parser, "linux"); |
117 | 0 | if (!val) |
118 | 0 | return glnx_throw (error, "aboot: no \"linux\" key in bootloader config"); |
119 | 0 | *vmlinuz = g_build_filename ("/boot", val, NULL); |
120 | |
|
121 | 0 | val = ostree_bootconfig_parser_get (parser, "initrd"); |
122 | 0 | if (!val) |
123 | 0 | return glnx_throw (error, "aboot: no \"initrd\" key in bootloader config"); |
124 | 0 | *initramfs = g_build_filename ("/boot", val, NULL); |
125 | |
|
126 | 0 | val = ostree_bootconfig_parser_get (parser, "options"); |
127 | 0 | if (!val) |
128 | 0 | return glnx_throw (error, "aboot: no \"options\" key in bootloader config"); |
129 | 0 | *options = g_strdup (val); |
130 | |
|
131 | 0 | return TRUE; |
132 | 0 | } |
133 | | |
134 | | static void |
135 | | child_setup_fchdir (gpointer data) |
136 | 0 | { |
137 | 0 | int fd = (int)(uintptr_t)data; |
138 | 0 | int rc = fchdir (fd); |
139 | 0 | g_assert (rc == 0); |
140 | 0 | } |
141 | | |
142 | | static gboolean |
143 | | _ostree_bootloader_aboot_post_bls_sync (OstreeBootloader *bootloader, int bootversion, |
144 | | GCancellable *cancellable, GError **error) |
145 | 0 | { |
146 | 0 | OstreeBootloaderAboot *self = OSTREE_BOOTLOADER_ABOOT (bootloader); |
147 | | |
148 | | /* Note that unlike the grub2-mkconfig backend, we make no attempt to |
149 | | * chroot(). |
150 | | */ |
151 | | // g_assert (self->sysroot->booted_deployment); |
152 | |
|
153 | 0 | if (!glnx_fstatat_allow_noent (self->sysroot->sysroot_fd, aboot_requires_execute_path, NULL, 0, |
154 | 0 | error)) |
155 | 0 | return FALSE; |
156 | | |
157 | | /* If there's no stamp file, nothing to do */ |
158 | 0 | if (errno == ENOENT) |
159 | 0 | return TRUE; |
160 | | |
161 | 0 | g_autofree gchar *aboot = NULL; |
162 | 0 | g_autofree gchar *abootcfg = NULL; |
163 | 0 | g_autofree gchar *version = NULL; |
164 | 0 | g_autofree gchar *vmlinuz = NULL; |
165 | 0 | g_autofree gchar *initramfs = NULL; |
166 | 0 | g_autofree gchar *options = NULL; |
167 | 0 | if (!_ostree_aboot_get_bls_config (self, bootversion, &aboot, &abootcfg, &version, &vmlinuz, |
168 | 0 | &initramfs, &options, cancellable, error)) |
169 | 0 | return FALSE; |
170 | | |
171 | 0 | const char *const aboot_argv[] |
172 | 0 | = { "aboot-deploy", "-r", ".", "-c", abootcfg, "-o", options, aboot, NULL }; |
173 | 0 | int estatus; |
174 | |
|
175 | 0 | if (!g_spawn_sync (NULL, (char **)aboot_argv, NULL, G_SPAWN_SEARCH_PATH, child_setup_fchdir, |
176 | 0 | (gpointer)(uintptr_t)self->sysroot->sysroot_fd, NULL, NULL, &estatus, error)) |
177 | 0 | { |
178 | 0 | return FALSE; |
179 | 0 | } |
180 | | |
181 | 0 | if (!g_spawn_check_exit_status (estatus, error)) |
182 | 0 | { |
183 | 0 | return FALSE; |
184 | 0 | } |
185 | | |
186 | 0 | if (!glnx_unlinkat (self->sysroot->sysroot_fd, aboot_requires_execute_path, 0, error)) |
187 | 0 | { |
188 | 0 | return FALSE; |
189 | 0 | } |
190 | | |
191 | 0 | return TRUE; |
192 | 0 | } |
193 | | |
194 | | static void |
195 | | _ostree_bootloader_aboot_finalize (GObject *object) |
196 | 0 | { |
197 | 0 | OstreeBootloaderAboot *self = OSTREE_BOOTLOADER_ABOOT (object); |
198 | |
|
199 | 0 | g_clear_object (&self->sysroot); |
200 | |
|
201 | 0 | G_OBJECT_CLASS (_ostree_bootloader_aboot_parent_class)->finalize (object); |
202 | 0 | } |
203 | | |
204 | | void |
205 | | _ostree_bootloader_aboot_init (OstreeBootloaderAboot *self) |
206 | 0 | { |
207 | 0 | } |
208 | | |
209 | | static void |
210 | | _ostree_bootloader_aboot_bootloader_iface_init (OstreeBootloaderInterface *iface) |
211 | 0 | { |
212 | 0 | iface->query = _ostree_bootloader_aboot_query; |
213 | 0 | iface->get_name = _ostree_bootloader_aboot_get_name; |
214 | 0 | iface->write_config = _ostree_bootloader_aboot_write_config; |
215 | 0 | iface->post_bls_sync = _ostree_bootloader_aboot_post_bls_sync; |
216 | 0 | } |
217 | | |
218 | | void |
219 | | _ostree_bootloader_aboot_class_init (OstreeBootloaderAbootClass *class) |
220 | 0 | { |
221 | 0 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
222 | |
|
223 | 0 | object_class->finalize = _ostree_bootloader_aboot_finalize; |
224 | 0 | } |
225 | | |
226 | | OstreeBootloaderAboot * |
227 | | _ostree_bootloader_aboot_new (OstreeSysroot *sysroot) |
228 | 0 | { |
229 | 0 | OstreeBootloaderAboot *self = g_object_new (OSTREE_TYPE_BOOTLOADER_ABOOT, NULL); |
230 | 0 | self->sysroot = g_object_ref (sysroot); |
231 | 0 | return self; |
232 | 0 | } |