]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_linux.c
47803de0c77bec64c3bf2119a692a9db2d8a8024
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / ventoy / ventoy_linux.c
1 /******************************************************************************
2 * ventoy_linux.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 #define VTOY_APPEND_EXT_SIZE 4096
42 static int g_append_ext_sector = 0;
43
44 char * ventoy_get_line(char *start)
45 {
46 if (start == NULL)
47 {
48 return NULL;
49 }
50
51 while (*start && *start != '\n')
52 {
53 start++;
54 }
55
56 if (*start == 0)
57 {
58 return NULL;
59 }
60 else
61 {
62 *start = 0;
63 return start + 1;
64 }
65 }
66
67 static initrd_info * ventoy_find_initrd_by_name(initrd_info *list, const char *name)
68 {
69 initrd_info *node = list;
70
71 while (node)
72 {
73 if (grub_strcmp(node->name, name) == 0)
74 {
75 return node;
76 }
77 node = node->next;
78 }
79
80 return NULL;
81 }
82
83 grub_err_t ventoy_cmd_clear_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args)
84 {
85 initrd_info *node = g_initrd_img_list;
86 initrd_info *next;
87
88 (void)ctxt;
89 (void)argc;
90 (void)args;
91
92 while (node)
93 {
94 next = node->next;
95 grub_free(node);
96 node = next;
97 }
98
99 g_initrd_img_list = NULL;
100 g_initrd_img_tail = NULL;
101 g_initrd_img_count = 0;
102 g_valid_initrd_count = 0;
103
104 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
105 }
106
107 grub_err_t ventoy_cmd_dump_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args)
108 {
109 int i = 0;
110 initrd_info *node = g_initrd_img_list;
111
112 (void)ctxt;
113 (void)argc;
114 (void)args;
115
116 grub_printf("###################\n");
117 grub_printf("initrd info list: valid count:%d\n", g_valid_initrd_count);
118
119 while (node)
120 {
121 grub_printf("%s ", node->size > 0 ? "*" : " ");
122 grub_printf("%02u %s offset:%llu size:%llu \n", i++, node->name, (unsigned long long)node->offset,
123 (unsigned long long)node->size);
124 node = node->next;
125 }
126
127 grub_printf("###################\n");
128
129 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
130 }
131
132 static void ventoy_parse_directory(char *path, char *dir, int buflen)
133 {
134 int end;
135 char *pos;
136
137 pos = grub_strstr(path, ")");
138 if (!pos)
139 {
140 pos = path;
141 }
142
143 end = grub_snprintf(dir, buflen, "%s", pos + 1);
144 while (end > 0)
145 {
146 if (dir[end] == '/')
147 {
148 dir[end + 1] = 0;
149 break;
150 }
151 end--;
152 }
153 }
154
155 static grub_err_t ventoy_isolinux_initrd_collect(grub_file_t file, const char *prefix)
156 {
157 int i = 0;
158 int offset;
159 int prefixlen = 0;
160 char *buf = NULL;
161 char *pos = NULL;
162 char *start = NULL;
163 char *nextline = NULL;
164 initrd_info *img = NULL;
165
166 prefixlen = grub_strlen(prefix);
167
168 buf = grub_zalloc(file->size + 2);
169 if (!buf)
170 {
171 return 0;
172 }
173
174 grub_file_read(file, buf, file->size);
175
176 for (start = buf; start; start = nextline)
177 {
178 nextline = ventoy_get_line(start);
179
180 while (ventoy_isspace(*start))
181 {
182 start++;
183 }
184
185 offset = 7; // strlen("initrd=") or "INITRD " or "initrd "
186 pos = grub_strstr(start, "initrd=");
187 if (pos == NULL)
188 {
189 pos = start;
190
191 if (grub_strncmp(start, "INITRD", 6) != 0 && grub_strncmp(start, "initrd", 6) != 0)
192 {
193 if (grub_strstr(start, "xen") &&
194 ((pos = grub_strstr(start, "--- /install.img")) != NULL ||
195 (pos = grub_strstr(start, "--- initrd.img")) != NULL
196 ))
197 {
198 offset = 4; // "--- "
199 }
200 else
201 {
202 continue;
203 }
204 }
205 }
206
207 pos += offset;
208
209 while (1)
210 {
211 i = 0;
212 img = grub_zalloc(sizeof(initrd_info));
213 if (!img)
214 {
215 break;
216 }
217
218 if (*pos != '/')
219 {
220 grub_strcpy(img->name, prefix);
221 i = prefixlen;
222 }
223
224 while (i < 255 && (0 == ventoy_is_word_end(*pos)))
225 {
226 img->name[i++] = *pos++;
227 }
228
229 if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
230 {
231 grub_free(img);
232 }
233 else
234 {
235 if (g_initrd_img_list)
236 {
237 img->prev = g_initrd_img_tail;
238 g_initrd_img_tail->next = img;
239 }
240 else
241 {
242 g_initrd_img_list = img;
243 }
244
245 g_initrd_img_tail = img;
246 g_initrd_img_count++;
247 }
248
249 if (*pos == ',')
250 {
251 pos++;
252 }
253 else
254 {
255 break;
256 }
257 }
258 }
259
260 grub_free(buf);
261 return GRUB_ERR_NONE;
262 }
263
264 static int ventoy_isolinux_initrd_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
265 {
266 grub_file_t file = NULL;
267 ventoy_initrd_ctx *ctx = (ventoy_initrd_ctx *)data;
268
269 (void)info;
270
271 if (NULL == grub_strstr(filename, ".cfg") && NULL == grub_strstr(filename, ".CFG"))
272 {
273 return 0;
274 }
275
276 debug("init hook dir <%s%s>\n", ctx->path_prefix, filename);
277
278 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", ctx->path_prefix, filename);
279 if (!file)
280 {
281 return 0;
282 }
283
284 ventoy_isolinux_initrd_collect(file, ctx->dir_prefix);
285 grub_file_close(file);
286
287 return 0;
288 }
289
290 grub_err_t ventoy_cmd_isolinux_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args)
291 {
292 grub_fs_t fs;
293 grub_device_t dev = NULL;
294 char *device_name = NULL;
295 ventoy_initrd_ctx ctx;
296 char directory[256];
297
298 (void)ctxt;
299 (void)argc;
300
301 device_name = grub_file_get_device_name(args[0]);
302 if (!device_name)
303 {
304 goto end;
305 }
306
307 dev = grub_device_open(device_name);
308 if (!dev)
309 {
310 goto end;
311 }
312
313 fs = grub_fs_probe(dev);
314 if (!fs)
315 {
316 goto end;
317 }
318
319 debug("isolinux initrd collect %s\n", args[0]);
320
321 ventoy_parse_directory(args[0], directory, sizeof(directory) - 1);
322 ctx.path_prefix = args[0];
323 ctx.dir_prefix = (argc > 1) ? args[1] : directory;
324
325 debug("path_prefix=<%s> dir_prefix=<%s>\n", ctx.path_prefix, ctx.dir_prefix);
326
327 fs->fs_dir(dev, directory, ventoy_isolinux_initrd_hook, &ctx);
328
329 end:
330 check_free(device_name, grub_free);
331 check_free(dev, grub_device_close);
332
333 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
334 }
335
336 static int ventoy_linux_initrd_collect_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
337 {
338 int len;
339 initrd_info *img = NULL;
340
341 (void)data;
342
343 if (0 == info->dir)
344 {
345 if (grub_strncmp(filename, "initrd", 6) == 0)
346 {
347 len = (int)grub_strlen(filename);
348 if (grub_strcmp(filename + len - 4, ".img") == 0)
349 {
350 img = grub_zalloc(sizeof(initrd_info));
351 if (img)
352 {
353 grub_snprintf(img->name, sizeof(img->name), "/boot/%s", filename);
354
355 if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
356 {
357 grub_free(img);
358 }
359 else
360 {
361 if (g_initrd_img_list)
362 {
363 img->prev = g_initrd_img_tail;
364 g_initrd_img_tail->next = img;
365 }
366 else
367 {
368 g_initrd_img_list = img;
369 }
370
371 g_initrd_img_tail = img;
372 g_initrd_img_count++;
373 }
374 }
375 }
376 }
377 }
378
379 return 0;
380 }
381
382 static int ventoy_linux_collect_boot_initrds(void)
383 {
384 grub_fs_t fs;
385 grub_device_t dev = NULL;
386
387 dev = grub_device_open("loop");
388 if (!dev)
389 {
390 debug("failed to open device loop\n");
391 goto end;
392 }
393
394 fs = grub_fs_probe(dev);
395 if (!fs)
396 {
397 debug("failed to probe fs %d\n", grub_errno);
398 goto end;
399 }
400
401 fs->fs_dir(dev, "/boot", ventoy_linux_initrd_collect_hook, NULL);
402
403 end:
404 return 0;
405 }
406
407 static grub_err_t ventoy_grub_cfg_initrd_collect(const char *fileName)
408 {
409 int i = 0;
410 int len = 0;
411 int dollar = 0;
412 int quotation = 0;
413 int initrd_dollar = 0;
414 grub_file_t file = NULL;
415 char *buf = NULL;
416 char *start = NULL;
417 char *nextline = NULL;
418 initrd_info *img = NULL;
419
420 debug("grub initrd collect %s\n", fileName);
421
422 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", fileName);
423 if (!file)
424 {
425 return 0;
426 }
427
428 buf = grub_zalloc(file->size + 2);
429 if (!buf)
430 {
431 grub_file_close(file);
432 return 0;
433 }
434
435 grub_file_read(file, buf, file->size);
436
437 for (start = buf; start; start = nextline)
438 {
439 nextline = ventoy_get_line(start);
440
441 while (ventoy_isspace(*start))
442 {
443 start++;
444 }
445
446 if (grub_strncmp(start, "initrd", 6) != 0)
447 {
448 continue;
449 }
450
451 start += 6;
452 while (*start && (!ventoy_isspace(*start)))
453 {
454 start++;
455 }
456
457 while (ventoy_isspace(*start))
458 {
459 start++;
460 }
461
462 if (*start == '"')
463 {
464 quotation = 1;
465 start++;
466 }
467
468 while (*start)
469 {
470 img = grub_zalloc(sizeof(initrd_info));
471 if (!img)
472 {
473 break;
474 }
475
476 dollar = 0;
477 for (i = 0; i < 255 && (0 == ventoy_is_word_end(*start)); i++)
478 {
479 img->name[i] = *start++;
480 if (img->name[i] == '$')
481 {
482 dollar = 1;
483 }
484 }
485
486 if (quotation)
487 {
488 len = (int)grub_strlen(img->name);
489 if (len > 2 && img->name[len - 1] == '"')
490 {
491 img->name[len - 1] = 0;
492 }
493 debug("Remove quotation <%s>\n", img->name);
494 }
495
496 /* special process for /boot/initrd$XXX.img */
497 if (dollar == 1)
498 {
499 if (grub_strncmp(img->name, "/boot/initrd$", 13) == 0)
500 {
501 len = (int)grub_strlen(img->name);
502 if (grub_strcmp(img->name + len - 4, ".img") == 0)
503 {
504 initrd_dollar++;
505 }
506 }
507 }
508
509 if (dollar == 1 || ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
510 {
511 grub_free(img);
512 }
513 else
514 {
515 if (g_initrd_img_list)
516 {
517 img->prev = g_initrd_img_tail;
518 g_initrd_img_tail->next = img;
519 }
520 else
521 {
522 g_initrd_img_list = img;
523 }
524
525 g_initrd_img_tail = img;
526 g_initrd_img_count++;
527 }
528
529 if (*start == ' ' || *start == '\t')
530 {
531 while (ventoy_isspace(*start))
532 {
533 start++;
534 }
535 }
536 else
537 {
538 break;
539 }
540 }
541 }
542
543 grub_free(buf);
544 grub_file_close(file);
545
546 if (initrd_dollar > 0 && grub_strncmp(fileName, "(loop)/", 7) == 0)
547 {
548 debug("collect initrd variable %d\n", initrd_dollar);
549 ventoy_linux_collect_boot_initrds();
550 }
551
552 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
553 }
554
555 static int ventoy_grub_initrd_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
556 {
557 char filePath[256];
558 ventoy_initrd_ctx *ctx = (ventoy_initrd_ctx *)data;
559
560 (void)info;
561
562 debug("ventoy_grub_initrd_hook %s\n", filename);
563
564 if (NULL == grub_strstr(filename, ".cfg") &&
565 NULL == grub_strstr(filename, ".CFG") &&
566 NULL == grub_strstr(filename, ".conf"))
567 {
568 return 0;
569 }
570
571 debug("init hook dir <%s%s>\n", ctx->path_prefix, filename);
572
573 grub_snprintf(filePath, sizeof(filePath) - 1, "%s%s", ctx->dir_prefix, filename);
574 ventoy_grub_cfg_initrd_collect(filePath);
575
576 return 0;
577 }
578
579 grub_err_t ventoy_cmd_grub_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args)
580 {
581 grub_fs_t fs;
582 grub_device_t dev = NULL;
583 char *device_name = NULL;
584 ventoy_initrd_ctx ctx;
585
586 (void)ctxt;
587 (void)argc;
588
589 if (argc != 2)
590 {
591 return 0;
592 }
593
594 debug("grub initrd collect %s %s\n", args[0], args[1]);
595
596 if (grub_strcmp(args[0], "file") == 0)
597 {
598 return ventoy_grub_cfg_initrd_collect(args[1]);
599 }
600
601 device_name = grub_file_get_device_name(args[1]);
602 if (!device_name)
603 {
604 debug("failed to get device name %s\n", args[1]);
605 goto end;
606 }
607
608 dev = grub_device_open(device_name);
609 if (!dev)
610 {
611 debug("failed to open device %s\n", device_name);
612 goto end;
613 }
614
615 fs = grub_fs_probe(dev);
616 if (!fs)
617 {
618 debug("failed to probe fs %d\n", grub_errno);
619 goto end;
620 }
621
622 ctx.dir_prefix = args[1];
623 ctx.path_prefix = grub_strstr(args[1], device_name);
624 if (ctx.path_prefix)
625 {
626 ctx.path_prefix += grub_strlen(device_name) + 1;
627 }
628 else
629 {
630 ctx.path_prefix = args[1];
631 }
632
633 debug("ctx.path_prefix:<%s>\n", ctx.path_prefix);
634
635 fs->fs_dir(dev, ctx.path_prefix, ventoy_grub_initrd_hook, &ctx);
636
637 end:
638 check_free(device_name, grub_free);
639 check_free(dev, grub_device_close);
640
641
642 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
643 }
644
645 grub_err_t ventoy_cmd_specify_initrd_file(grub_extcmd_context_t ctxt, int argc, char **args)
646 {
647 initrd_info *img = NULL;
648
649 (void)ctxt;
650 (void)argc;
651
652 debug("ventoy_cmd_specify_initrd_file %s\n", args[0]);
653
654 img = grub_zalloc(sizeof(initrd_info));
655 if (!img)
656 {
657 return 1;
658 }
659
660 grub_strncpy(img->name, args[0], sizeof(img->name));
661 if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
662 {
663 debug("%s is already exist\n", args[0]);
664 grub_free(img);
665 }
666 else
667 {
668 if (g_initrd_img_list)
669 {
670 img->prev = g_initrd_img_tail;
671 g_initrd_img_tail->next = img;
672 }
673 else
674 {
675 g_initrd_img_list = img;
676 }
677
678 g_initrd_img_tail = img;
679 g_initrd_img_count++;
680 }
681
682 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
683 }
684
685 static int ventoy_cpio_newc_get_int(char *value)
686 {
687 char buf[16] = {0};
688
689 grub_memcpy(buf, value, 8);
690 return (int)grub_strtoul(buf, NULL, 16);
691 }
692
693 static void ventoy_cpio_newc_fill_int(grub_uint32_t value, char *buf, int buflen)
694 {
695 int i;
696 int len;
697 char intbuf[32];
698
699 len = grub_snprintf(intbuf, sizeof(intbuf), "%x", value);
700
701 for (i = 0; i < buflen; i++)
702 {
703 buf[i] = '0';
704 }
705
706 if (len > buflen)
707 {
708 grub_printf("int buf len overflow %d %d\n", len, buflen);
709 }
710 else
711 {
712 grub_memcpy(buf + buflen - len, intbuf, len);
713 }
714 }
715
716 int ventoy_cpio_newc_fill_head(void *buf, int filesize, const void *filedata, const char *name)
717 {
718 int namelen = 0;
719 int headlen = 0;
720 static grub_uint32_t cpio_ino = 0xFFFFFFF0;
721 cpio_newc_header *cpio = (cpio_newc_header *)buf;
722
723 namelen = grub_strlen(name) + 1;
724 headlen = sizeof(cpio_newc_header) + namelen;
725 headlen = ventoy_align(headlen, 4);
726
727 grub_memset(cpio, '0', sizeof(cpio_newc_header));
728 grub_memset(cpio + 1, 0, headlen - sizeof(cpio_newc_header));
729
730 grub_memcpy(cpio->c_magic, "070701", 6);
731 ventoy_cpio_newc_fill_int(cpio_ino--, cpio->c_ino, 8);
732 ventoy_cpio_newc_fill_int(0100777, cpio->c_mode, 8);
733 ventoy_cpio_newc_fill_int(1, cpio->c_nlink, 8);
734 ventoy_cpio_newc_fill_int(filesize, cpio->c_filesize, 8);
735 ventoy_cpio_newc_fill_int(namelen, cpio->c_namesize, 8);
736 grub_memcpy(cpio + 1, name, namelen);
737
738 if (filedata)
739 {
740 grub_memcpy((char *)cpio + headlen, filedata, filesize);
741 }
742
743 return headlen;
744 }
745
746 static grub_uint32_t ventoy_linux_get_virt_chunk_count(void)
747 {
748 int i;
749 grub_uint32_t count = g_valid_initrd_count;
750
751 if (g_conf_replace_count > 0)
752 {
753 for (i = 0; i < g_conf_replace_count; i++)
754 {
755 if (g_conf_replace_offset[i] > 0)
756 {
757 count++;
758 }
759 }
760 }
761
762 if (g_append_ext_sector > 0)
763 {
764 count++;
765 }
766
767 return count;
768 }
769
770 static grub_uint32_t ventoy_linux_get_virt_chunk_size(void)
771 {
772 int i;
773 grub_uint32_t size;
774
775 size = (sizeof(ventoy_virt_chunk) + g_ventoy_cpio_size) * g_valid_initrd_count;
776
777 if (g_conf_replace_count > 0)
778 {
779 for (i = 0; i < g_conf_replace_count; i++)
780 {
781 if (g_conf_replace_offset[i] > 0)
782 {
783 size += sizeof(ventoy_virt_chunk) + g_conf_replace_new_len_align[i];
784 }
785 }
786 }
787
788 if (g_append_ext_sector > 0)
789 {
790 size += sizeof(ventoy_virt_chunk) + VTOY_APPEND_EXT_SIZE;
791 }
792
793 return size;
794 }
795
796 static void ventoy_linux_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
797 {
798 int i = 0;
799 int id = 0;
800 int virtid = 0;
801 initrd_info *node;
802 grub_uint64_t sector;
803 grub_uint32_t offset;
804 grub_uint32_t cpio_secs;
805 grub_uint32_t initrd_secs;
806 char *override;
807 ventoy_virt_chunk *cur;
808 ventoy_grub_param_file_replace *replace = NULL;
809 char name[32];
810
811 override = (char *)chain + chain->virt_chunk_offset;
812 sector = (isosize + 2047) / 2048;
813 cpio_secs = g_ventoy_cpio_size / 2048;
814
815 offset = ventoy_linux_get_virt_chunk_count() * sizeof(ventoy_virt_chunk);
816 cur = (ventoy_virt_chunk *)override;
817
818 for (node = g_initrd_img_list; node; node = node->next)
819 {
820 if (node->size == 0)
821 {
822 continue;
823 }
824
825 initrd_secs = (grub_uint32_t)((node->size + 2047) / 2048);
826
827 cur->mem_sector_start = sector;
828 cur->mem_sector_end = cur->mem_sector_start + cpio_secs;
829 cur->mem_sector_offset = offset;
830 cur->remap_sector_start = cur->mem_sector_end;
831 cur->remap_sector_end = cur->remap_sector_start + initrd_secs;
832 cur->org_sector_start = (grub_uint32_t)(node->offset / 2048);
833
834 grub_memcpy(g_ventoy_runtime_buf, &chain->os_param, sizeof(ventoy_os_param));
835
836 grub_memset(name, 0, 16);
837 grub_snprintf(name, sizeof(name), "initrd%03d", ++id);
838
839 grub_memcpy(g_ventoy_initrd_head + 1, name, 16);
840 ventoy_cpio_newc_fill_int((grub_uint32_t)node->size, g_ventoy_initrd_head->c_filesize, 8);
841
842 grub_memcpy(override + offset, g_ventoy_cpio_buf, g_ventoy_cpio_size);
843
844 chain->virt_img_size_in_bytes += g_ventoy_cpio_size + initrd_secs * 2048;
845
846 offset += g_ventoy_cpio_size;
847 sector += cpio_secs + initrd_secs;
848 cur++;
849 virtid++;
850 }
851
852 /* Lenovo EasyStartup need an addional sector for boundary check */
853 if (g_append_ext_sector > 0)
854 {
855 cpio_secs = VTOY_APPEND_EXT_SIZE / 2048;
856
857 cur->mem_sector_start = sector;
858 cur->mem_sector_end = cur->mem_sector_start + cpio_secs;
859 cur->mem_sector_offset = offset;
860 cur->remap_sector_start = 0;
861 cur->remap_sector_end = 0;
862 cur->org_sector_start = 0;
863
864 grub_memset(override + offset, 0, VTOY_APPEND_EXT_SIZE);
865
866 chain->virt_img_size_in_bytes += VTOY_APPEND_EXT_SIZE;
867
868 offset += VTOY_APPEND_EXT_SIZE;
869 sector += cpio_secs;
870 cur++;
871 virtid++;
872 }
873
874 if (g_conf_replace_count > 0)
875 {
876 for (i = 0; i < g_conf_replace_count; i++)
877 {
878 if (g_conf_replace_offset[i] > 0)
879 {
880 cpio_secs = g_conf_replace_new_len_align[i] / 2048;
881
882 cur->mem_sector_start = sector;
883 cur->mem_sector_end = cur->mem_sector_start + cpio_secs;
884 cur->mem_sector_offset = offset;
885 cur->remap_sector_start = 0;
886 cur->remap_sector_end = 0;
887 cur->org_sector_start = 0;
888
889 grub_memcpy(override + offset, g_conf_replace_new_buf[i], g_conf_replace_new_len[i]);
890
891 chain->virt_img_size_in_bytes += g_conf_replace_new_len_align[i];
892
893 replace = g_grub_param->img_replace + i;
894 if (replace->magic == GRUB_IMG_REPLACE_MAGIC)
895 {
896 replace->new_file_virtual_id = virtid;
897 }
898
899 offset += g_conf_replace_new_len_align[i];
900 sector += cpio_secs;
901 cur++;
902 virtid++;
903 }
904 }
905 }
906
907 return;
908 }
909
910 static grub_uint32_t ventoy_linux_get_override_chunk_count(void)
911 {
912 int i;
913 grub_uint32_t count = g_valid_initrd_count;
914
915 if (g_conf_replace_count > 0)
916 {
917 for (i = 0; i < g_conf_replace_count; i++)
918 {
919 if (g_conf_replace_offset[i] > 0)
920 {
921 count++;
922 }
923 }
924 }
925
926 if (g_svd_replace_offset > 0)
927 {
928 count++;
929 }
930
931 return count;
932 }
933
934 static grub_uint32_t ventoy_linux_get_override_chunk_size(void)
935 {
936 int i;
937 int count = g_valid_initrd_count;
938
939 if (g_conf_replace_count > 0)
940 {
941 for (i = 0; i < g_conf_replace_count; i++)
942 {
943 if (g_conf_replace_offset[i] > 0)
944 {
945 count++;
946 }
947 }
948 }
949
950 if (g_svd_replace_offset > 0)
951 {
952 count++;
953 }
954
955 return sizeof(ventoy_override_chunk) * count;
956 }
957
958 static void ventoy_linux_fill_override_data( grub_uint64_t isosize, void *override)
959 {
960 int i;
961 initrd_info *node;
962 grub_uint32_t mod;
963 grub_uint32_t newlen;
964 grub_uint64_t sector;
965 ventoy_override_chunk *cur;
966 ventoy_iso9660_override *dirent;
967 ventoy_udf_override *udf;
968
969 sector = (isosize + 2047) / 2048;
970
971 cur = (ventoy_override_chunk *)override;
972 for (node = g_initrd_img_list; node; node = node->next)
973 {
974 if (node->size == 0)
975 {
976 continue;
977 }
978
979 newlen = (grub_uint32_t)(node->size + g_ventoy_cpio_size);
980 mod = newlen % 4;
981 if (mod > 0)
982 {
983 newlen += 4 - mod; /* cpio must align with 4 */
984 }
985
986 if (node->iso_type == 0)
987 {
988 dirent = (ventoy_iso9660_override *)node->override_data;
989
990 node->override_length = sizeof(ventoy_iso9660_override);
991 dirent->first_sector = (grub_uint32_t)sector;
992 dirent->size = newlen;
993 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
994 dirent->size_be = grub_swap_bytes32(dirent->size);
995
996 sector += (dirent->size + 2047) / 2048;
997 }
998 else
999 {
1000 udf = (ventoy_udf_override *)node->override_data;
1001
1002 node->override_length = sizeof(ventoy_udf_override);
1003 udf->length = newlen;
1004 udf->position = (grub_uint32_t)sector - node->udf_start_block;
1005
1006 sector += (udf->length + 2047) / 2048;
1007 }
1008
1009 cur->img_offset = node->override_offset;
1010 cur->override_size = node->override_length;
1011 grub_memcpy(cur->override_data, node->override_data, cur->override_size);
1012 cur++;
1013 }
1014
1015 if (g_conf_replace_count > 0)
1016 {
1017 for (i = 0; i < g_conf_replace_count; i++)
1018 {
1019 if (g_conf_replace_offset[i] > 0)
1020 {
1021 cur->img_offset = g_conf_replace_offset[i];
1022 cur->override_size = sizeof(ventoy_iso9660_override);
1023
1024 newlen = (grub_uint32_t)(g_conf_replace_new_len[i]);
1025
1026 dirent = (ventoy_iso9660_override *)cur->override_data;
1027 dirent->first_sector = (grub_uint32_t)sector;
1028 dirent->size = newlen;
1029 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
1030 dirent->size_be = grub_swap_bytes32(dirent->size);
1031
1032 sector += (dirent->size + 2047) / 2048;
1033 cur++;
1034 }
1035 }
1036 }
1037
1038 if (g_svd_replace_offset > 0)
1039 {
1040 cur->img_offset = g_svd_replace_offset;
1041 cur->override_size = 1;
1042 cur->override_data[0] = 0xFF;
1043 cur++;
1044 }
1045
1046 return;
1047 }
1048
1049 grub_err_t ventoy_cmd_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args)
1050 {
1051 char buf[32] = {0};
1052
1053 (void)ctxt;
1054 (void)argc;
1055 (void)args;
1056
1057 if (argc == 1)
1058 {
1059 grub_snprintf(buf, sizeof(buf), "%d", g_initrd_img_count);
1060 grub_env_set(args[0], buf);
1061 }
1062
1063 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1064 }
1065
1066 grub_err_t ventoy_cmd_valid_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args)
1067 {
1068 char buf[32] = {0};
1069
1070 (void)ctxt;
1071 (void)argc;
1072 (void)args;
1073
1074 if (argc == 1)
1075 {
1076 grub_snprintf(buf, sizeof(buf), "%d", g_valid_initrd_count);
1077 grub_env_set(args[0], buf);
1078 }
1079
1080 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1081 }
1082
1083 static grub_err_t ventoy_linux_locate_initrd(int filt, int *filtcnt)
1084 {
1085 int data;
1086 int filtbysize = 1;
1087 int sizefilt = 0;
1088 grub_file_t file;
1089 initrd_info *node;
1090
1091 debug("ventoy_linux_locate_initrd %d\n", filt);
1092
1093 g_valid_initrd_count = 0;
1094
1095 if (grub_env_get("INITRD_NO_SIZE_FILT"))
1096 {
1097 filtbysize = 0;
1098 }
1099
1100 for (node = g_initrd_img_list; node; node = node->next)
1101 {
1102 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(loop)%s", node->name);
1103 if (!file)
1104 {
1105 continue;
1106 }
1107
1108 debug("file <%s> size:%d\n", node->name, (int)file->size);
1109
1110 /* initrd file too small */
1111 if (filtbysize
1112 && (NULL == grub_strstr(node->name, "minirt.gz"))
1113 && (NULL == grub_strstr(node->name, "initrd.xz"))
1114 )
1115 {
1116 if (filt > 0 && file->size <= g_ventoy_cpio_size + 2048)
1117 {
1118 debug("file size too small %d\n", (int)g_ventoy_cpio_size);
1119 grub_file_close(file);
1120 sizefilt++;
1121 continue;
1122 }
1123 }
1124
1125 /* skip hdt.img */
1126 if (file->size <= VTOY_SIZE_1MB && grub_strcmp(node->name, "/boot/hdt.img") == 0)
1127 {
1128 continue;
1129 }
1130
1131 if (grub_strcmp(file->fs->name, "iso9660") == 0)
1132 {
1133 node->iso_type = 0;
1134 node->override_offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
1135
1136 grub_file_read(file, &data, 1); // just read for hook trigger
1137 node->offset = grub_iso9660_get_last_read_pos(file);
1138 }
1139 else
1140 {
1141 /* TBD */
1142 }
1143
1144 node->size = file->size;
1145 g_valid_initrd_count++;
1146
1147 grub_file_close(file);
1148 }
1149
1150 *filtcnt = sizefilt;
1151
1152 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1153 }
1154
1155
1156 grub_err_t ventoy_cmd_linux_get_main_initrd_index(grub_extcmd_context_t ctxt, int argc, char **args)
1157 {
1158 int index = 0;
1159 char buf[32];
1160 initrd_info *node = NULL;
1161
1162 (void)ctxt;
1163 (void)argc;
1164 (void)args;
1165
1166 if (argc != 1)
1167 {
1168 return 1;
1169 }
1170
1171 if (g_initrd_img_count == 1)
1172 {
1173 ventoy_set_env(args[0], "0");
1174 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1175 }
1176
1177 for (node = g_initrd_img_list; node; node = node->next)
1178 {
1179 if (node->size <= 0)
1180 {
1181 continue;
1182 }
1183
1184 if (grub_strstr(node->name, "ucode") || grub_strstr(node->name, "-firmware"))
1185 {
1186 index++;
1187 continue;
1188 }
1189
1190 grub_snprintf(buf, sizeof(buf), "%d", index);
1191 ventoy_set_env(args[0], buf);
1192 break;
1193 }
1194
1195 debug("main initrd index:%d\n", index);
1196
1197 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1198 }
1199
1200 grub_err_t ventoy_cmd_linux_locate_initrd(grub_extcmd_context_t ctxt, int argc, char **args)
1201 {
1202 int sizefilt = 0;
1203
1204 (void)ctxt;
1205 (void)argc;
1206 (void)args;
1207
1208 ventoy_linux_locate_initrd(1, &sizefilt);
1209
1210 if (g_valid_initrd_count == 0 && sizefilt > 0)
1211 {
1212 ventoy_linux_locate_initrd(0, &sizefilt);
1213 }
1214
1215 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1216 }
1217
1218 static int ventoy_cpio_busybox64(cpio_newc_header *head, const char *file)
1219 {
1220 char *name;
1221 int namelen;
1222 int offset;
1223 int count = 0;
1224 char filepath[128];
1225
1226 grub_snprintf(filepath, sizeof(filepath), "ventoy/busybox/%s", file);
1227
1228 name = (char *)(head + 1);
1229 while (name[0] && count < 2)
1230 {
1231 if (grub_strcmp(name, "ventoy/busybox/ash") == 0)
1232 {
1233 grub_memcpy(name, "ventoy/busybox/32h", 18);
1234 count++;
1235 }
1236 else if (grub_strcmp(name, filepath) == 0)
1237 {
1238 grub_memcpy(name, "ventoy/busybox/ash", 18);
1239 count++;
1240 }
1241
1242 namelen = ventoy_cpio_newc_get_int(head->c_namesize);
1243 offset = sizeof(cpio_newc_header) + namelen;
1244 offset = ventoy_align(offset, 4);
1245 offset += ventoy_cpio_newc_get_int(head->c_filesize);
1246 offset = ventoy_align(offset, 4);
1247
1248 head = (cpio_newc_header *)((char *)head + offset);
1249 name = (char *)(head + 1);
1250 }
1251
1252 return 0;
1253 }
1254
1255
1256 grub_err_t ventoy_cmd_cpio_busybox_64(grub_extcmd_context_t ctxt, int argc, char **args)
1257 {
1258 (void)ctxt;
1259 (void)argc;
1260 (void)args;
1261
1262 debug("ventoy_cmd_busybox_64 %d\n", argc);
1263 ventoy_cpio_busybox64((cpio_newc_header *)g_ventoy_cpio_buf, args[0]);
1264 return 0;
1265 }
1266
1267 grub_err_t ventoy_cmd_skip_svd(grub_extcmd_context_t ctxt, int argc, char **args)
1268 {
1269 int i;
1270 grub_file_t file;
1271 char buf[16];
1272
1273 (void)ctxt;
1274 (void)argc;
1275
1276 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
1277 if (!file)
1278 {
1279 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]);
1280 }
1281
1282 for (i = 0; i < 10; i++)
1283 {
1284 buf[0] = 0;
1285 grub_file_seek(file, (17 + i) * 2048);
1286 grub_file_read(file, buf, 16);
1287
1288 if (buf[0] == 2 && grub_strncmp(buf + 1, "CD001", 5) == 0)
1289 {
1290 debug("Find SVD at VD %d\n", i);
1291 g_svd_replace_offset = (17 + i) * 2048;
1292 break;
1293 }
1294 }
1295
1296 if (i >= 10)
1297 {
1298 debug("SVD not found %d\n", (int)g_svd_replace_offset);
1299 }
1300
1301 grub_file_close(file);
1302
1303 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1304 }
1305
1306 grub_err_t ventoy_cmd_append_ext_sector(grub_extcmd_context_t ctxt, int argc, char **args)
1307 {
1308 (void)ctxt;
1309 (void)argc;
1310 (void)args;
1311
1312 if (args[0][0] == '1')
1313 {
1314 g_append_ext_sector = 1;
1315 }
1316 else
1317 {
1318 g_append_ext_sector = 0;
1319 }
1320
1321 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1322 }
1323
1324 grub_err_t ventoy_cmd_load_cpio(grub_extcmd_context_t ctxt, int argc, char **args)
1325 {
1326 int i;
1327 int rc;
1328 char *pos = NULL;
1329 char *template_file = NULL;
1330 char *template_buf = NULL;
1331 char *persistent_buf = NULL;
1332 char *injection_buf = NULL;
1333 dud *dudnode = NULL;
1334 char tmpname[128];
1335 const char *injection_file = NULL;
1336 grub_uint8_t *buf = NULL;
1337 grub_uint32_t mod;
1338 grub_uint32_t headlen;
1339 grub_uint32_t initrd_head_len;
1340 grub_uint32_t padlen;
1341 grub_uint32_t img_chunk_size;
1342 grub_uint32_t template_size = 0;
1343 grub_uint32_t persistent_size = 0;
1344 grub_uint32_t injection_size = 0;
1345 grub_uint32_t dud_size = 0;
1346 grub_file_t file;
1347 grub_file_t archfile;
1348 grub_file_t tmpfile;
1349 install_template *template_node = NULL;
1350 ventoy_img_chunk_list chunk_list;
1351
1352 (void)ctxt;
1353 (void)argc;
1354
1355 if (argc != 4)
1356 {
1357 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s cpiofile\n", cmd_raw_name);
1358 }
1359
1360 if (g_img_chunk_list.chunk == NULL || g_img_chunk_list.cur_chunk == 0)
1361 {
1362 return grub_error(GRUB_ERR_BAD_ARGUMENT, "image chunk is null\n");
1363 }
1364
1365 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1366
1367 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s/%s", args[0], VTOY_COMM_CPIO);
1368 if (!file)
1369 {
1370 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s/%s\n", args[0], VTOY_COMM_CPIO);
1371 }
1372
1373 archfile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s/%s", args[0], VTOY_ARCH_CPIO);
1374 if (!archfile)
1375 {
1376 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s/%s\n", args[0], VTOY_ARCH_CPIO);
1377 grub_file_close(file);
1378 }
1379
1380 debug("load %s %s success\n", VTOY_COMM_CPIO, VTOY_ARCH_CPIO);
1381
1382 if (g_ventoy_cpio_buf)
1383 {
1384 grub_free(g_ventoy_cpio_buf);
1385 g_ventoy_cpio_buf = NULL;
1386 g_ventoy_cpio_size = 0;
1387 }
1388
1389 rc = ventoy_plugin_get_persistent_chunklist(args[1], -1, &chunk_list);
1390 if (rc == 0 && chunk_list.cur_chunk > 0 && chunk_list.chunk)
1391 {
1392 persistent_size = chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1393 persistent_buf = (char *)(chunk_list.chunk);
1394 }
1395
1396 template_file = ventoy_plugin_get_cur_install_template(args[1], &template_node);
1397 if (template_file)
1398 {
1399 debug("auto install template: <%s> <addr:%p> <len:%d>\n",
1400 template_file, template_node->filebuf, template_node->filelen);
1401
1402 template_size = template_node->filelen;
1403 template_buf = grub_malloc(template_size);
1404 if (template_buf)
1405 {
1406 grub_memcpy(template_buf, template_node->filebuf, template_size);
1407 }
1408 }
1409 else
1410 {
1411 debug("auto install script skipped or not configed %s\n", args[1]);
1412 }
1413
1414 injection_file = ventoy_plugin_get_injection(args[1]);
1415 if (injection_file)
1416 {
1417 debug("injection archive: <%s>\n", injection_file);
1418 tmpfile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", args[2], injection_file);
1419 if (tmpfile)
1420 {
1421 debug("injection archive size:%d\n", (int)tmpfile->size);
1422 injection_size = tmpfile->size;
1423 injection_buf = grub_malloc(injection_size);
1424 if (injection_buf)
1425 {
1426 grub_file_read(tmpfile, injection_buf, injection_size);
1427 }
1428
1429 grub_file_close(tmpfile);
1430 }
1431 else
1432 {
1433 debug("Failed to open injection archive %s%s\n", args[2], injection_file);
1434 }
1435 }
1436 else
1437 {
1438 debug("injection not configed %s\n", args[1]);
1439 }
1440
1441 dudnode = ventoy_plugin_find_dud(args[1]);
1442 if (dudnode)
1443 {
1444 debug("dud file: <%d>\n", dudnode->dudnum);
1445 ventoy_plugin_load_dud(dudnode, args[2]);
1446 for (i = 0; i < dudnode->dudnum; i++)
1447 {
1448 if (dudnode->files[i].size > 0)
1449 {
1450 dud_size += dudnode->files[i].size + sizeof(cpio_newc_header);
1451 }
1452 }
1453 }
1454 else
1455 {
1456 debug("dud not configed %s\n", args[1]);
1457 }
1458
1459 g_ventoy_cpio_buf = grub_malloc(file->size + archfile->size + 40960 + template_size +
1460 persistent_size + injection_size + dud_size + img_chunk_size);
1461 if (NULL == g_ventoy_cpio_buf)
1462 {
1463 grub_file_close(file);
1464 grub_file_close(archfile);
1465 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't alloc memory %llu\n", file->size);
1466 }
1467
1468 grub_file_read(file, g_ventoy_cpio_buf, file->size);
1469 buf = (grub_uint8_t *)(g_ventoy_cpio_buf + file->size - 4);
1470 while (*((grub_uint32_t *)buf) != 0x37303730)
1471 {
1472 buf -= 4;
1473 }
1474
1475 grub_file_read(archfile, buf, archfile->size);
1476 buf += (archfile->size - 4);
1477 while (*((grub_uint32_t *)buf) != 0x37303730)
1478 {
1479 buf -= 4;
1480 }
1481
1482 /* get initrd head len */
1483 initrd_head_len = ventoy_cpio_newc_fill_head(buf, 0, NULL, "initrd000.xx");
1484
1485 /* step1: insert image chunk data to cpio */
1486 headlen = ventoy_cpio_newc_fill_head(buf, img_chunk_size, g_img_chunk_list.chunk, "ventoy/ventoy_image_map");
1487 buf += headlen + ventoy_align(img_chunk_size, 4);
1488
1489 if (template_buf)
1490 {
1491 headlen = ventoy_cpio_newc_fill_head(buf, template_size, template_buf, "ventoy/autoinstall");
1492 buf += headlen + ventoy_align(template_size, 4);
1493 grub_check_free(template_buf);
1494 }
1495
1496 if (persistent_size > 0 && persistent_buf)
1497 {
1498 headlen = ventoy_cpio_newc_fill_head(buf, persistent_size, persistent_buf, "ventoy/ventoy_persistent_map");
1499 buf += headlen + ventoy_align(persistent_size, 4);
1500 grub_check_free(persistent_buf);
1501 }
1502
1503 if (injection_size > 0 && injection_buf)
1504 {
1505 headlen = ventoy_cpio_newc_fill_head(buf, injection_size, injection_buf, "ventoy/ventoy_injection");
1506 buf += headlen + ventoy_align(injection_size, 4);
1507
1508 grub_free(injection_buf);
1509 injection_buf = NULL;
1510 }
1511
1512 if (dud_size > 0)
1513 {
1514 for (i = 0; i < dudnode->dudnum; i++)
1515 {
1516 pos = grub_strrchr(dudnode->dudpath[i].path, '.');
1517 grub_snprintf(tmpname, sizeof(tmpname), "ventoy/ventoy_dud%d%s", i, (pos ? pos : ".iso"));
1518 dud_size = dudnode->files[i].size;
1519 headlen = ventoy_cpio_newc_fill_head(buf, dud_size, dudnode->files[i].buf, tmpname);
1520 buf += headlen + ventoy_align(dud_size, 4);
1521 }
1522 }
1523
1524 /* step2: insert os param to cpio */
1525 headlen = ventoy_cpio_newc_fill_head(buf, 0, NULL, "ventoy/ventoy_os_param");
1526 padlen = sizeof(ventoy_os_param);
1527 g_ventoy_cpio_size = (grub_uint32_t)(buf - g_ventoy_cpio_buf) + headlen + padlen + initrd_head_len;
1528 mod = g_ventoy_cpio_size % 2048;
1529 if (mod)
1530 {
1531 g_ventoy_cpio_size += 2048 - mod;
1532 padlen += 2048 - mod;
1533 }
1534
1535 /* update os param data size, the data will be updated before chain boot */
1536 ventoy_cpio_newc_fill_int(padlen, ((cpio_newc_header *)buf)->c_filesize, 8);
1537 g_ventoy_runtime_buf = (grub_uint8_t *)buf + headlen;
1538
1539 /* step3: fill initrd cpio head, the file size will be updated before chain boot */
1540 g_ventoy_initrd_head = (cpio_newc_header *)(g_ventoy_runtime_buf + padlen);
1541 ventoy_cpio_newc_fill_head(g_ventoy_initrd_head, 0, NULL, "initrd000.xx");
1542
1543 grub_file_close(file);
1544 grub_file_close(archfile);
1545
1546 if (grub_strcmp(args[3], "busybox=64") == 0)
1547 {
1548 debug("cpio busybox proc %s\n", args[3]);
1549 ventoy_cpio_busybox64((cpio_newc_header *)g_ventoy_cpio_buf, "64h");
1550 }
1551 else if (grub_strcmp(args[3], "busybox=a64") == 0)
1552 {
1553 debug("cpio busybox proc %s\n", args[3]);
1554 ventoy_cpio_busybox64((cpio_newc_header *)g_ventoy_cpio_buf, "a64");
1555 }
1556 else if (grub_strcmp(args[3], "busybox=m64") == 0)
1557 {
1558 debug("cpio busybox proc %s\n", args[3]);
1559 ventoy_cpio_busybox64((cpio_newc_header *)g_ventoy_cpio_buf, "m64");
1560 }
1561
1562 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1563 }
1564
1565 grub_err_t ventoy_cmd_trailer_cpio(grub_extcmd_context_t ctxt, int argc, char **args)
1566 {
1567 int mod;
1568 int bufsize;
1569 int namelen;
1570 int offset;
1571 char *name;
1572 grub_uint8_t *bufend;
1573 cpio_newc_header *head;
1574 grub_file_t file;
1575 const grub_uint8_t trailler[124] = {
1576 0x30, 0x37, 0x30, 0x37, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1577 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1578 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30,
1579 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1580 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1581 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1582 0x30, 0x30, 0x30, 0x30, 0x30, 0x42, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x54, 0x52,
1583 0x41, 0x49, 0x4C, 0x45, 0x52, 0x21, 0x21, 0x21, 0x00, 0x00, 0x00, 0x00
1584 };
1585
1586 (void)ctxt;
1587 (void)argc;
1588
1589 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", args[0], args[1]);
1590 if (!file)
1591 {
1592 return 1;
1593 }
1594
1595 grub_memset(g_ventoy_runtime_buf, 0, sizeof(ventoy_os_param));
1596 ventoy_fill_os_param(file, (ventoy_os_param *)g_ventoy_runtime_buf);
1597
1598 grub_file_close(file);
1599
1600 grub_memcpy(g_ventoy_initrd_head, trailler, sizeof(trailler));
1601 bufend = (grub_uint8_t *)g_ventoy_initrd_head + sizeof(trailler);
1602
1603 bufsize = (int)(bufend - g_ventoy_cpio_buf);
1604 mod = bufsize % 512;
1605 if (mod)
1606 {
1607 grub_memset(bufend, 0, 512 - mod);
1608 bufsize += 512 - mod;
1609 }
1610
1611 if (argc > 1 && grub_strcmp(args[2], "noinit") == 0)
1612 {
1613 head = (cpio_newc_header *)g_ventoy_cpio_buf;
1614 name = (char *)(head + 1);
1615
1616 while (grub_strcmp(name, "TRAILER!!!"))
1617 {
1618 if (grub_strcmp(name, "init") == 0)
1619 {
1620 grub_memcpy(name, "xxxx", 4);
1621 }
1622 else if (grub_strcmp(name, "linuxrc") == 0)
1623 {
1624 grub_memcpy(name, "vtoyxrc", 7);
1625 }
1626 else if (grub_strcmp(name, "sbin") == 0)
1627 {
1628 grub_memcpy(name, "vtoy", 4);
1629 }
1630 else if (grub_strcmp(name, "sbin/init") == 0)
1631 {
1632 grub_memcpy(name, "vtoy/vtoy", 9);
1633 }
1634
1635 namelen = ventoy_cpio_newc_get_int(head->c_namesize);
1636 offset = sizeof(cpio_newc_header) + namelen;
1637 offset = ventoy_align(offset, 4);
1638 offset += ventoy_cpio_newc_get_int(head->c_filesize);
1639 offset = ventoy_align(offset, 4);
1640
1641 head = (cpio_newc_header *)((char *)head + offset);
1642 name = (char *)(head + 1);
1643 }
1644 }
1645
1646 ventoy_memfile_env_set("ventoy_cpio", g_ventoy_cpio_buf, (ulonglong)bufsize);
1647
1648 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1649 }
1650
1651 grub_err_t ventoy_cmd_linux_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
1652 {
1653 int len = 0;
1654 int ventoy_compatible = 0;
1655 grub_uint32_t size = 0;
1656 grub_uint64_t isosize = 0;
1657 grub_uint32_t boot_catlog = 0;
1658 grub_uint32_t img_chunk_size = 0;
1659 grub_uint32_t override_count = 0;
1660 grub_uint32_t override_size = 0;
1661 grub_uint32_t virt_chunk_count = 0;
1662 grub_uint32_t virt_chunk_size = 0;
1663 grub_file_t file;
1664 grub_disk_t disk;
1665 const char *pLastChain = NULL;
1666 const char *compatible;
1667 ventoy_chain_head *chain;
1668
1669 (void)ctxt;
1670 (void)argc;
1671
1672 compatible = grub_env_get("ventoy_compatible");
1673 if (compatible && compatible[0] == 'Y')
1674 {
1675 ventoy_compatible = 1;
1676 }
1677
1678 if ((NULL == g_img_chunk_list.chunk) || (0 == ventoy_compatible && g_ventoy_cpio_buf == NULL))
1679 {
1680 grub_printf("ventoy not ready\n");
1681 return 1;
1682 }
1683
1684 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
1685 if (!file)
1686 {
1687 return 1;
1688 }
1689
1690 isosize = file->size;
1691
1692 len = (int)grub_strlen(args[0]);
1693 if (len >= 4 && 0 == grub_strcasecmp(args[0] + len - 4, ".img"))
1694 {
1695 debug("boot catlog %u for img file\n", boot_catlog);
1696 }
1697 else
1698 {
1699 boot_catlog = ventoy_get_iso_boot_catlog(file);
1700 if (boot_catlog)
1701 {
1702 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
1703 {
1704 grub_env_set("LoadIsoEfiDriver", "on");
1705 }
1706 }
1707 else
1708 {
1709 if (ventoy_is_efi_os())
1710 {
1711 grub_env_set("LoadIsoEfiDriver", "on");
1712 }
1713 else
1714 {
1715 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
1716 }
1717 }
1718 }
1719
1720 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1721
1722 override_count = ventoy_linux_get_override_chunk_count();
1723 virt_chunk_count = ventoy_linux_get_virt_chunk_count();
1724
1725 if (ventoy_compatible)
1726 {
1727 size = sizeof(ventoy_chain_head) + img_chunk_size;
1728 }
1729 else
1730 {
1731 override_size = ventoy_linux_get_override_chunk_size();
1732 virt_chunk_size = ventoy_linux_get_virt_chunk_size();
1733 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
1734 }
1735
1736 pLastChain = grub_env_get("vtoy_chain_mem_addr");
1737 if (pLastChain)
1738 {
1739 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
1740 if (chain)
1741 {
1742 debug("free last chain memory %p\n", chain);
1743 grub_free(chain);
1744 }
1745 }
1746
1747 chain = ventoy_alloc_chain(size);
1748 if (!chain)
1749 {
1750 grub_printf("Failed to alloc chain linux memory size %u\n", size);
1751 grub_file_close(file);
1752 return 1;
1753 }
1754
1755 ventoy_memfile_env_set("vtoy_chain_mem", chain, (ulonglong)size);
1756
1757 grub_memset(chain, 0, sizeof(ventoy_chain_head));
1758
1759 /* part 1: os parameter */
1760 g_ventoy_chain_type = ventoy_chain_linux;
1761 ventoy_fill_os_param(file, &(chain->os_param));
1762
1763 /* part 2: chain head */
1764 disk = file->device->disk;
1765 chain->disk_drive = disk->id;
1766 chain->disk_sector_size = (1 << disk->log_sector_size);
1767 chain->real_img_size_in_bytes = file->size;
1768 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
1769 chain->boot_catalog = boot_catlog;
1770
1771 if (!ventoy_is_efi_os())
1772 {
1773 grub_file_seek(file, boot_catlog * 2048);
1774 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
1775 }
1776
1777 /* part 3: image chunk */
1778 chain->img_chunk_offset = sizeof(ventoy_chain_head);
1779 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
1780 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
1781
1782 if (ventoy_compatible)
1783 {
1784 return 0;
1785 }
1786
1787 /* part 4: override chunk */
1788 if (override_count > 0)
1789 {
1790 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
1791 chain->override_chunk_num = override_count;
1792 ventoy_linux_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
1793 }
1794
1795 /* part 5: virt chunk */
1796 if (virt_chunk_count > 0)
1797 {
1798 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
1799 chain->virt_chunk_num = virt_chunk_count;
1800 ventoy_linux_fill_virt_data(isosize, chain);
1801 }
1802
1803 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1804 }
1805
1806 static char *ventoy_systemd_conf_tag(char *buf, const char *tag, int optional)
1807 {
1808 int taglen = 0;
1809 char *start = NULL;
1810 char *nextline = NULL;
1811
1812 taglen = grub_strlen(tag);
1813 for (start = buf; start; start = nextline)
1814 {
1815 nextline = ventoy_get_line(start);
1816 while (ventoy_isspace(*start))
1817 {
1818 start++;
1819 }
1820
1821 if (grub_strncmp(start, tag, taglen) == 0 && (start[taglen] == ' ' || start[taglen] == '\t'))
1822 {
1823 start += taglen;
1824 while (ventoy_isspace(*start))
1825 {
1826 start++;
1827 }
1828 return start;
1829 }
1830 }
1831
1832 if (optional == 0)
1833 {
1834 debug("tag<%s> NOT found\n", tag);
1835 }
1836 return NULL;
1837 }
1838
1839 static int ventoy_systemd_conf_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
1840 {
1841 int oldpos = 0;
1842 char *tag = NULL;
1843 char *bkbuf = NULL;
1844 char *filebuf = NULL;
1845 grub_file_t file = NULL;
1846 systemd_menu_ctx *ctx = (systemd_menu_ctx *)data;
1847
1848 debug("ventoy_systemd_conf_hook %s\n", filename);
1849
1850 if (info->dir || NULL == grub_strstr(filename, ".conf"))
1851 {
1852 return 0;
1853 }
1854
1855
1856 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s/loader/entries/%s", ctx->dev, filename);
1857 if (!file)
1858 {
1859 return 0;
1860 }
1861
1862 filebuf = grub_zalloc(2 * file->size + 8);
1863 if (!filebuf)
1864 {
1865 goto out;
1866 }
1867
1868 bkbuf = filebuf + file->size + 4;
1869 grub_file_read(file, bkbuf, file->size);
1870
1871 oldpos = ctx->pos;
1872
1873 /* title --> menuentry */
1874 grub_memcpy(filebuf, bkbuf, file->size);
1875 tag = ventoy_systemd_conf_tag(filebuf, "title", 0);
1876 vtoy_check_goto_out(tag);
1877 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, "menuentry \"%s\" {\n", tag);
1878
1879 /* linux xxx */
1880 grub_memcpy(filebuf, bkbuf, file->size);
1881 tag = ventoy_systemd_conf_tag(filebuf, "linux", 0);
1882 if (!tag)
1883 {
1884 ctx->pos = oldpos;
1885 goto out;
1886 }
1887 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, " echo \"Downloading kernel ...\"\n linux %s ", tag);
1888
1889 /* kernel options */
1890 grub_memcpy(filebuf, bkbuf, file->size);
1891 tag = ventoy_systemd_conf_tag(filebuf, "options", 0);
1892 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, "%s \n", tag ? tag : "");
1893
1894
1895 /* initrd xxx xxx xxx */
1896 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, " echo \"Downloading initrd ...\"\n initrd ");
1897 grub_memcpy(filebuf, bkbuf, file->size);
1898 tag = ventoy_systemd_conf_tag(filebuf, "initrd", 1);
1899 while (tag)
1900 {
1901 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, "%s ", tag);
1902 tag = ventoy_systemd_conf_tag(tag + grub_strlen(tag) + 1, "initrd", 1);
1903 }
1904
1905 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, "\n boot\n}\n");
1906
1907 out:
1908 grub_check_free(filebuf);
1909 grub_file_close(file);
1910 return 0;
1911 }
1912
1913 grub_err_t ventoy_cmd_linux_systemd_menu(grub_extcmd_context_t ctxt, int argc, char **args)
1914 {
1915 static char *buf = NULL;
1916 grub_fs_t fs;
1917 char *device_name = NULL;
1918 grub_device_t dev = NULL;
1919 systemd_menu_ctx ctx;
1920
1921 (void)ctxt;
1922 (void)argc;
1923
1924 if (!buf)
1925 {
1926 buf = grub_malloc(VTOY_LINUX_SYSTEMD_MENU_MAX_BUF);
1927 if (!buf)
1928 {
1929 goto end;
1930 }
1931 }
1932
1933 device_name = grub_file_get_device_name(args[0]);
1934 if (!device_name)
1935 {
1936 debug("failed to get device name %s\n", args[0]);
1937 goto end;
1938 }
1939
1940 dev = grub_device_open(device_name);
1941 if (!dev)
1942 {
1943 debug("failed to open device %s\n", device_name);
1944 goto end;
1945 }
1946
1947 fs = grub_fs_probe(dev);
1948 if (!fs)
1949 {
1950 debug("failed to probe fs %d\n", grub_errno);
1951 goto end;
1952 }
1953
1954 ctx.dev = args[0];
1955 ctx.buf = buf;
1956 ctx.pos = 0;
1957 ctx.len = VTOY_LINUX_SYSTEMD_MENU_MAX_BUF;
1958 fs->fs_dir(dev, "/loader/entries", ventoy_systemd_conf_hook, &ctx);
1959
1960 ventoy_memfile_env_set(args[1], buf, (ulonglong)(ctx.pos));
1961
1962 end:
1963 grub_check_free(device_name);
1964 check_free(dev, grub_device_close);
1965 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1966 }
1967
1968 static int ventoy_limine_path_convert(char *path)
1969 {
1970 char newpath[256] = {0};
1971
1972 if (grub_strncmp(path, "boot://2/", 9) == 0)
1973 {
1974 grub_snprintf(newpath, sizeof(newpath), "(vtimghd,2)/%s", path + 9);
1975 }
1976 else if (grub_strncmp(path, "boot://1/", 9) == 0)
1977 {
1978 grub_snprintf(newpath, sizeof(newpath), "(vtimghd,1)/%s", path + 9);
1979 }
1980
1981 if (newpath[0])
1982 {
1983 grub_snprintf(path, 1024, "%s", newpath);
1984 }
1985
1986 return 0;
1987 }
1988
1989 grub_err_t ventoy_cmd_linux_limine_menu(grub_extcmd_context_t ctxt, int argc, char **args)
1990 {
1991 int pos = 0;
1992 int sub = 0;
1993 int len = VTOY_LINUX_SYSTEMD_MENU_MAX_BUF;
1994 char *filebuf = NULL;
1995 char *start = NULL;
1996 char *nextline = NULL;
1997 grub_file_t file = NULL;
1998 char *title = NULL;
1999 char *kernel = NULL;
2000 char *initrd = NULL;
2001 char *param = NULL;
2002 static char *buf = NULL;
2003
2004 (void)ctxt;
2005 (void)argc;
2006
2007 if (!buf)
2008 {
2009 buf = grub_malloc(len + 4 * 1024);
2010 if (!buf)
2011 {
2012 goto end;
2013 }
2014 }
2015
2016 title = buf + len;
2017 kernel = title + 1024;
2018 initrd = kernel + 1024;
2019 param = initrd + 1024;
2020
2021 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, args[0]);
2022 if (!file)
2023 {
2024 return 0;
2025 }
2026
2027 filebuf = grub_zalloc(file->size + 8);
2028 if (!filebuf)
2029 {
2030 goto end;
2031 }
2032
2033 grub_file_read(file, filebuf, file->size);
2034 grub_file_close(file);
2035
2036
2037 title[0] = kernel[0] = initrd[0] = param[0] = 0;
2038 for (start = filebuf; start; start = nextline)
2039 {
2040 nextline = ventoy_get_line(start);
2041 while (ventoy_isspace(*start))
2042 {
2043 start++;
2044 }
2045
2046 if (start[0] == ':')
2047 {
2048 if (start[1] == ':')
2049 {
2050 grub_snprintf(title, 1024, "%s", start + 2);
2051 }
2052 else
2053 {
2054 if (sub)
2055 {
2056 vtoy_len_ssprintf(buf, pos, len, "}\n");
2057 sub = 0;
2058 }
2059
2060 if (nextline && nextline[0] == ':' && nextline[1] == ':')
2061 {
2062 vtoy_len_ssprintf(buf, pos, len, "submenu \"[+] %s\" {\n", start + 2);
2063 sub = 1;
2064 title[0] = 0;
2065 }
2066 else
2067 {
2068 grub_snprintf(title, 1024, "%s", start + 1);
2069 }
2070 }
2071 }
2072 else if (grub_strncmp(start, "KERNEL_PATH=", 12) == 0)
2073 {
2074 grub_snprintf(kernel, 1024, "%s", start + 12);
2075 }
2076 else if (grub_strncmp(start, "MODULE_PATH=", 12) == 0)
2077 {
2078 grub_snprintf(initrd, 1024, "%s", start + 12);
2079 }
2080 else if (grub_strncmp(start, "KERNEL_CMDLINE=", 15) == 0)
2081 {
2082 grub_snprintf(param, 1024, "%s", start + 15);
2083 }
2084
2085 if (title[0] && kernel[0] && initrd[0] && param[0])
2086 {
2087 ventoy_limine_path_convert(kernel);
2088 ventoy_limine_path_convert(initrd);
2089
2090 vtoy_len_ssprintf(buf, pos, len, "menuentry \"%s\" {\n", title);
2091 vtoy_len_ssprintf(buf, pos, len, " echo \"Downloading kernel ...\"\n linux %s %s\n", kernel, param);
2092 vtoy_len_ssprintf(buf, pos, len, " echo \"Downloading initrd ...\"\n initrd %s\n", initrd);
2093 vtoy_len_ssprintf(buf, pos, len, "}\n");
2094
2095 title[0] = kernel[0] = initrd[0] = param[0] = 0;
2096 }
2097 }
2098
2099 if (sub)
2100 {
2101 vtoy_len_ssprintf(buf, pos, len, "}\n");
2102 sub = 0;
2103 }
2104
2105 ventoy_memfile_env_set(args[1], buf, (ulonglong)pos);
2106
2107 end:
2108 grub_check_free(filebuf);
2109 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
2110 }
2111