]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_unix.c
1e81309c934ad343195e823983fad71f15e5d97e
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / ventoy / ventoy_unix.c
1 /******************************************************************************
2 * ventoy_unix.c
3 *
4 * Copyright (c) 2020, longpanda <admin@ventoy.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20 #include <grub/types.h>
21 #include <grub/misc.h>
22 #include <grub/mm.h>
23 #include <grub/err.h>
24 #include <grub/dl.h>
25 #include <grub/disk.h>
26 #include <grub/device.h>
27 #include <grub/term.h>
28 #include <grub/partition.h>
29 #include <grub/file.h>
30 #include <grub/normal.h>
31 #include <grub/extcmd.h>
32 #include <grub/datetime.h>
33 #include <grub/i18n.h>
34 #include <grub/net.h>
35 #include <grub/time.h>
36 #include <grub/ventoy.h>
37 #include "ventoy_def.h"
38
39 GRUB_MOD_LICENSE ("GPLv3+");
40
41 char g_ko_mod_path[256];
42 int g_conf_new_len = 0;
43 char *g_conf_new_data = NULL;
44
45 int g_mod_new_len = 0;
46 char *g_mod_new_data = NULL;
47
48 grub_uint64_t g_mod_override_offset = 0;
49 grub_uint64_t g_conf_override_offset = 0;
50
51 static int ventoy_get_file_override(const char *filename, grub_uint64_t *offset)
52 {
53 grub_file_t file;
54
55 *offset = 0;
56
57 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(loop)%s", filename);
58 if (!file)
59 {
60 return 1;
61 }
62
63 *offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
64
65 grub_file_close(file);
66
67 return 0;
68 }
69
70 static grub_uint32_t ventoy_unix_get_override_chunk_count(void)
71 {
72 grub_uint32_t count = 0;
73
74 if (g_conf_new_len > 0)
75 {
76 count++;
77 }
78
79 if (g_mod_new_len > 0)
80 {
81 count++;
82 }
83
84 return count;
85 }
86
87 static grub_uint32_t ventoy_unix_get_virt_chunk_count(void)
88 {
89 grub_uint32_t count = 0;
90
91 if (g_conf_new_len > 0)
92 {
93 count++;
94 }
95
96 if (g_mod_new_len > 0)
97 {
98 count++;
99 }
100
101 return count;
102 }
103 static grub_uint32_t ventoy_unix_get_virt_chunk_size(void)
104 {
105 grub_uint32_t size;
106
107 size = sizeof(ventoy_virt_chunk) * ventoy_unix_get_virt_chunk_count();
108
109 if (g_conf_new_len > 0)
110 {
111 size += ventoy_align_2k(g_conf_new_len);
112 }
113
114 if (g_mod_new_len > 0)
115 {
116 size += ventoy_align_2k(g_mod_new_len);
117 }
118
119 return size;
120 }
121
122 static void ventoy_unix_fill_override_data( grub_uint64_t isosize, void *override)
123 {
124 grub_uint64_t sector;
125 ventoy_override_chunk *cur;
126 ventoy_iso9660_override *dirent;
127
128 sector = (isosize + 2047) / 2048;
129
130 cur = (ventoy_override_chunk *)override;
131
132 if (g_conf_new_len > 0)
133 {
134 /* loader.conf */
135 cur->img_offset = g_conf_override_offset;
136 cur->override_size = sizeof(ventoy_iso9660_override);
137 dirent = (ventoy_iso9660_override *)cur->override_data;
138 dirent->first_sector = (grub_uint32_t)sector;
139 dirent->size = (grub_uint32_t)g_conf_new_len;
140 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
141 dirent->size_be = grub_swap_bytes32(dirent->size);
142 sector += (dirent->size + 2047) / 2048;
143 }
144
145 if (g_mod_new_len > 0)
146 {
147 /* mod.ko */
148 cur++;
149 cur->img_offset = g_mod_override_offset;
150 cur->override_size = sizeof(ventoy_iso9660_override);
151 dirent = (ventoy_iso9660_override *)cur->override_data;
152 dirent->first_sector = (grub_uint32_t)sector;
153 dirent->size = (grub_uint32_t)g_mod_new_len;
154 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
155 dirent->size_be = grub_swap_bytes32(dirent->size);
156 sector += (dirent->size + 2047) / 2048;
157 }
158
159 return;
160 }
161
162 static void ventoy_unix_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
163 {
164 grub_uint64_t sector;
165 grub_uint32_t offset;
166 grub_uint32_t data_secs;
167 char *override;
168 ventoy_virt_chunk *cur;
169
170 override = (char *)chain + chain->virt_chunk_offset;
171 cur = (ventoy_virt_chunk *)override;
172
173 sector = (isosize + 2047) / 2048;
174 offset = 2 * sizeof(ventoy_virt_chunk);
175
176 if (g_conf_new_len > 0)
177 {
178 ventoy_unix_fill_virt(g_conf_new_data, g_conf_new_len);
179 }
180
181 if (g_mod_new_len > 0)
182 {
183 ventoy_unix_fill_virt(g_mod_new_data, g_mod_new_len);
184 }
185
186 return;
187 }
188
189 static int ventoy_freebsd_append_conf(char *buf, const char *isopath)
190 {
191 int pos = 0;
192 grub_uint32_t i;
193 grub_disk_t disk;
194 grub_file_t isofile;
195 char uuid[64] = {0};
196 ventoy_img_chunk *chunk;
197 grub_uint8_t disk_guid[16];
198
199 debug("ventoy_freebsd_append_conf %s\n", isopath);
200
201 isofile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", isopath);
202 if (!isofile)
203 {
204 return 1;
205 }
206
207 vtoy_ssprintf(buf, pos, "ventoy_load=\"%s\"\n", "YES");
208 vtoy_ssprintf(buf, pos, "ventoy_name=\"%s\"\n", g_ko_mod_path);
209
210 disk = isofile->device->disk;
211
212 ventoy_get_disk_guid(isofile->name, disk_guid);
213
214 for (i = 0; i < 16; i++)
215 {
216 grub_snprintf(uuid + i * 2, sizeof(uuid), "%02x", disk_guid[i]);
217 }
218
219 vtoy_ssprintf(buf, pos, "hint.ventoy.0.disksize=%llu\n", (ulonglong)(disk->total_sectors * (1 << disk->log_sector_size)));
220 vtoy_ssprintf(buf, pos, "hint.ventoy.0.diskuuid=\"%s\"\n", uuid);
221 vtoy_ssprintf(buf, pos, "hint.ventoy.0.segnum=%u\n", g_img_chunk_list.cur_chunk);
222
223 for (i = 0; i < g_img_chunk_list.cur_chunk; i++)
224 {
225 chunk = g_img_chunk_list.chunk + i;
226 vtoy_ssprintf(buf, pos, "hint.ventoy.%u.seg=\"0x%llx@0x%llx\"\n",
227 i, (ulonglong)(chunk->disk_start_sector * 512),
228 (ulonglong)((chunk->disk_end_sector + 1) * 512));
229 }
230
231 grub_file_close(isofile);
232
233 return pos;
234 }
235
236 grub_err_t ventoy_cmd_unix_reset(grub_extcmd_context_t ctxt, int argc, char **args)
237 {
238 (void)ctxt;
239 (void)argc;
240 (void)args;
241
242 g_conf_new_len = 0;
243 g_mod_new_len = 0;
244 g_mod_override_offset = 0;
245 g_conf_override_offset = 0;
246
247 check_free(g_mod_new_data, grub_free);
248 check_free(g_conf_new_data, grub_free);
249
250 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
251 }
252
253 grub_err_t ventoy_cmd_parse_freenas_ver(grub_extcmd_context_t ctxt, int argc, char **args)
254 {
255 grub_file_t file;
256 const char *ver = NULL;
257 char *buf = NULL;
258 VTOY_JSON *json = NULL;
259
260 (void)ctxt;
261 (void)argc;
262
263 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
264 if (!file)
265 {
266 debug("Failed to open file %s\n", args[0]);
267 return 1;
268 }
269
270 buf = grub_malloc(file->size + 2);
271 if (!buf)
272 {
273 grub_file_close(file);
274 return 0;
275 }
276 grub_file_read(file, buf, file->size);
277 buf[file->size] = 0;
278
279 json = vtoy_json_create();
280 if (!json)
281 {
282 goto end;
283 }
284
285 if (vtoy_json_parse(json, buf))
286 {
287 goto end;
288 }
289
290 ver = vtoy_json_get_string_ex(json->pstChild, "Version");
291 if (ver)
292 {
293 debug("freenas version:<%s>\n", ver);
294 ventoy_set_env(args[1], ver);
295 }
296 else
297 {
298 debug("freenas version:<%s>\n", "NOT FOUND");
299 grub_env_unset(args[1]);
300 }
301
302 end:
303 grub_check_free(buf);
304 check_free(json, vtoy_json_destroy);
305 grub_file_close(file);
306
307 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
308 }
309
310 grub_err_t ventoy_cmd_unix_freebsd_ver(grub_extcmd_context_t ctxt, int argc, char **args)
311 {
312 grub_file_t file;
313 char *buf;
314 char *start = NULL;
315 char *nextline = NULL;
316
317 (void)ctxt;
318 (void)argc;
319 (void)args;
320
321 file = ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD, "%s", args[0]);
322 if (!file)
323 {
324 debug("Failed to open file %s\n", args[0]);
325 return 1;
326 }
327
328 buf = grub_zalloc(file->size + 2);
329 if (!buf)
330 {
331 grub_file_close(file);
332 return 0;
333 }
334 grub_file_read(file, buf, file->size);
335
336 for (start = buf; start; start = nextline)
337 {
338 if (grub_strncmp(start, "USERLAND_VERSION", 16) == 0)
339 {
340 nextline = start;
341 while (*nextline && *nextline != '\r' && *nextline != '\n')
342 {
343 nextline++;
344 }
345
346 *nextline = 0;
347 break;
348 }
349 nextline = ventoy_get_line(start);
350 }
351
352 if (start)
353 {
354 debug("freebsd version:<%s>\n", start);
355 ventoy_set_env(args[1], start);
356 }
357 else
358 {
359 debug("freebsd version:<%s>\n", "NOT FOUND");
360 grub_env_unset(args[1]);
361 }
362
363 grub_free(buf);
364 grub_file_close(file);
365
366 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
367 }
368
369 grub_err_t ventoy_cmd_unix_replace_conf(grub_extcmd_context_t ctxt, int argc, char **args)
370 {
371 grub_uint32_t i;
372 char *data;
373 grub_uint64_t offset;
374 grub_file_t file;
375 const char *confile = NULL;
376 const char * loader_conf[] =
377 {
378 "/boot/loader.conf",
379 "/boot/defaults/loader.conf",
380 };
381
382 (void)ctxt;
383
384 if (argc != 2)
385 {
386 debug("Replace conf invalid argc %d\n", argc);
387 return 1;
388 }
389
390 for (i = 0; i < sizeof(loader_conf) / sizeof(loader_conf[0]); i++)
391 {
392 if (ventoy_get_file_override(loader_conf[i], &offset) == 0)
393 {
394 confile = loader_conf[i];
395 g_conf_override_offset = offset;
396 break;
397 }
398 }
399
400 if (confile == NULL)
401 {
402 debug("Can't find loader.conf file from %u locations\n", i);
403 return 1;
404 }
405
406 file = ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD, "(loop)/%s", confile);
407 if (!file)
408 {
409 debug("Failed to open %s \n", confile);
410 return 1;
411 }
412
413 debug("old conf file size:%d\n", (int)file->size);
414
415 data = grub_malloc(VTOY_MAX_SCRIPT_BUF);
416 if (!data)
417 {
418 grub_file_close(file);
419 return 1;
420 }
421
422 grub_file_read(file, data, file->size);
423 grub_file_close(file);
424
425 g_conf_new_data = data;
426 g_conf_new_len = (int)file->size;
427
428 if (grub_strcmp(args[0], "FreeBSD") == 0)
429 {
430 g_conf_new_len += ventoy_freebsd_append_conf(data + file->size, args[1]);
431 }
432
433 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
434 }
435
436 grub_err_t ventoy_cmd_unix_replace_ko(grub_extcmd_context_t ctxt, int argc, char **args)
437 {
438 char *data;
439 grub_uint64_t offset;
440 grub_file_t file;
441
442 (void)ctxt;
443
444 if (argc != 2)
445 {
446 debug("Replace ko invalid argc %d\n", argc);
447 return 1;
448 }
449
450 debug("replace ko %s\n", args[0]);
451
452 if (ventoy_get_file_override(args[0], &offset) == 0)
453 {
454 grub_snprintf(g_ko_mod_path, sizeof(g_ko_mod_path), "%s", args[0]);
455 g_mod_override_offset = offset;
456 }
457 else
458 {
459 debug("Can't find replace ko file from %s\n", args[0]);
460 return 1;
461 }
462
463 file = ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD, "%s", args[1]);
464 if (!file)
465 {
466 debug("Failed to open %s \n", args[1]);
467 return 1;
468 }
469
470 debug("new ko file size:%d\n", (int)file->size);
471
472 data = grub_malloc(file->size);
473 if (!data)
474 {
475 grub_file_close(file);
476 return 1;
477 }
478
479 grub_file_read(file, data, file->size);
480 grub_file_close(file);
481
482 g_mod_new_data = data;
483 g_mod_new_len = (int)file->size;
484
485 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
486 }
487
488 grub_err_t ventoy_cmd_unix_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
489 {
490 int ventoy_compatible = 0;
491 grub_uint32_t size = 0;
492 grub_uint64_t isosize = 0;
493 grub_uint32_t boot_catlog = 0;
494 grub_uint32_t img_chunk_size = 0;
495 grub_uint32_t override_count = 0;
496 grub_uint32_t override_size = 0;
497 grub_uint32_t virt_chunk_size = 0;
498 grub_file_t file;
499 grub_disk_t disk;
500 const char *pLastChain = NULL;
501 const char *compatible;
502 ventoy_chain_head *chain;
503 char envbuf[64];
504
505 (void)ctxt;
506 (void)argc;
507
508 compatible = grub_env_get("ventoy_compatible");
509 if (compatible && compatible[0] == 'Y')
510 {
511 ventoy_compatible = 1;
512 }
513
514 if (NULL == g_img_chunk_list.chunk)
515 {
516 grub_printf("ventoy not ready\n");
517 return 1;
518 }
519
520 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
521 if (!file)
522 {
523 return 1;
524 }
525
526 isosize = file->size;
527
528 boot_catlog = ventoy_get_iso_boot_catlog(file);
529 if (boot_catlog)
530 {
531 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
532 {
533 grub_env_set("LoadIsoEfiDriver", "on");
534 }
535 }
536 else
537 {
538 if (ventoy_is_efi_os())
539 {
540 grub_env_set("LoadIsoEfiDriver", "on");
541 }
542 else
543 {
544 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
545 }
546 }
547
548 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
549
550 if (ventoy_compatible)
551 {
552 size = sizeof(ventoy_chain_head) + img_chunk_size;
553 }
554 else
555 {
556 override_count = ventoy_unix_get_override_chunk_count();
557 override_size = override_count * sizeof(ventoy_override_chunk);
558
559 virt_chunk_size = ventoy_unix_get_virt_chunk_size();
560 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
561 }
562
563 pLastChain = grub_env_get("vtoy_chain_mem_addr");
564 if (pLastChain)
565 {
566 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
567 if (chain)
568 {
569 debug("free last chain memory %p\n", chain);
570 grub_free(chain);
571 }
572 }
573
574 chain = grub_malloc(size);
575 if (!chain)
576 {
577 grub_printf("Failed to alloc chain memory size %u\n", size);
578 grub_file_close(file);
579 return 1;
580 }
581
582 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
583 grub_env_set("vtoy_chain_mem_addr", envbuf);
584 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
585 grub_env_set("vtoy_chain_mem_size", envbuf);
586
587 grub_memset(chain, 0, sizeof(ventoy_chain_head));
588
589 /* part 1: os parameter */
590 g_ventoy_chain_type = ventoy_chain_linux;
591 ventoy_fill_os_param(file, &(chain->os_param));
592
593 /* part 2: chain head */
594 disk = file->device->disk;
595 chain->disk_drive = disk->id;
596 chain->disk_sector_size = (1 << disk->log_sector_size);
597 chain->real_img_size_in_bytes = file->size;
598 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
599 chain->boot_catalog = boot_catlog;
600
601 if (!ventoy_is_efi_os())
602 {
603 grub_file_seek(file, boot_catlog * 2048);
604 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
605 }
606
607 /* part 3: image chunk */
608 chain->img_chunk_offset = sizeof(ventoy_chain_head);
609 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
610 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
611
612 if (ventoy_compatible)
613 {
614 return 0;
615 }
616
617 /* part 4: override chunk */
618 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
619 chain->override_chunk_num = override_count;
620 ventoy_unix_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
621
622 /* part 5: virt chunk */
623 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
624 chain->virt_chunk_num = ventoy_unix_get_virt_chunk_count();
625 ventoy_unix_fill_virt_data(isosize, chain);
626
627 grub_file_close(file);
628
629 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
630 }
631