]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_linux.c
bfeb85893e08b916fb7fa5f0c3e7e783d5476d12
[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 char value[64];
1576 const grub_uint8_t trailler[124] = {
1577 0x30, 0x37, 0x30, 0x37, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1578 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1579 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 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, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1583 0x30, 0x30, 0x30, 0x30, 0x30, 0x42, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x54, 0x52,
1584 0x41, 0x49, 0x4C, 0x45, 0x52, 0x21, 0x21, 0x21, 0x00, 0x00, 0x00, 0x00
1585 };
1586
1587 (void)ctxt;
1588 (void)argc;
1589
1590 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", args[0], args[1]);
1591 if (!file)
1592 {
1593 return 1;
1594 }
1595
1596 grub_memset(g_ventoy_runtime_buf, 0, sizeof(ventoy_os_param));
1597 ventoy_fill_os_param(file, (ventoy_os_param *)g_ventoy_runtime_buf);
1598
1599 grub_file_close(file);
1600
1601 grub_memcpy(g_ventoy_initrd_head, trailler, sizeof(trailler));
1602 bufend = (grub_uint8_t *)g_ventoy_initrd_head + sizeof(trailler);
1603
1604 bufsize = (int)(bufend - g_ventoy_cpio_buf);
1605 mod = bufsize % 512;
1606 if (mod)
1607 {
1608 grub_memset(bufend, 0, 512 - mod);
1609 bufsize += 512 - mod;
1610 }
1611
1612 if (argc > 1 && grub_strcmp(args[2], "noinit") == 0)
1613 {
1614 head = (cpio_newc_header *)g_ventoy_cpio_buf;
1615 name = (char *)(head + 1);
1616
1617 while (grub_strcmp(name, "TRAILER!!!"))
1618 {
1619 if (grub_strcmp(name, "init") == 0)
1620 {
1621 grub_memcpy(name, "xxxx", 4);
1622 }
1623 else if (grub_strcmp(name, "linuxrc") == 0)
1624 {
1625 grub_memcpy(name, "vtoyxrc", 7);
1626 }
1627 else if (grub_strcmp(name, "sbin") == 0)
1628 {
1629 grub_memcpy(name, "vtoy", 4);
1630 }
1631 else if (grub_strcmp(name, "sbin/init") == 0)
1632 {
1633 grub_memcpy(name, "vtoy/vtoy", 9);
1634 }
1635
1636 namelen = ventoy_cpio_newc_get_int(head->c_namesize);
1637 offset = sizeof(cpio_newc_header) + namelen;
1638 offset = ventoy_align(offset, 4);
1639 offset += ventoy_cpio_newc_get_int(head->c_filesize);
1640 offset = ventoy_align(offset, 4);
1641
1642 head = (cpio_newc_header *)((char *)head + offset);
1643 name = (char *)(head + 1);
1644 }
1645 }
1646
1647 grub_snprintf(value, sizeof(value), "0x%llx", (ulonglong)(ulong)g_ventoy_cpio_buf);
1648 ventoy_set_env("ventoy_cpio_addr", value);
1649 grub_snprintf(value, sizeof(value), "%d", bufsize);
1650 ventoy_set_env("ventoy_cpio_size", value);
1651
1652 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1653 }
1654
1655 grub_err_t ventoy_cmd_linux_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
1656 {
1657 int len = 0;
1658 int ventoy_compatible = 0;
1659 grub_uint32_t size = 0;
1660 grub_uint64_t isosize = 0;
1661 grub_uint32_t boot_catlog = 0;
1662 grub_uint32_t img_chunk_size = 0;
1663 grub_uint32_t override_count = 0;
1664 grub_uint32_t override_size = 0;
1665 grub_uint32_t virt_chunk_count = 0;
1666 grub_uint32_t virt_chunk_size = 0;
1667 grub_file_t file;
1668 grub_disk_t disk;
1669 const char *pLastChain = NULL;
1670 const char *compatible;
1671 ventoy_chain_head *chain;
1672 char envbuf[64];
1673
1674 (void)ctxt;
1675 (void)argc;
1676
1677 compatible = grub_env_get("ventoy_compatible");
1678 if (compatible && compatible[0] == 'Y')
1679 {
1680 ventoy_compatible = 1;
1681 }
1682
1683 if ((NULL == g_img_chunk_list.chunk) || (0 == ventoy_compatible && g_ventoy_cpio_buf == NULL))
1684 {
1685 grub_printf("ventoy not ready\n");
1686 return 1;
1687 }
1688
1689 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
1690 if (!file)
1691 {
1692 return 1;
1693 }
1694
1695 isosize = file->size;
1696
1697 len = (int)grub_strlen(args[0]);
1698 if (len >= 4 && 0 == grub_strcasecmp(args[0] + len - 4, ".img"))
1699 {
1700 debug("boot catlog %u for img file\n", boot_catlog);
1701 }
1702 else
1703 {
1704 boot_catlog = ventoy_get_iso_boot_catlog(file);
1705 if (boot_catlog)
1706 {
1707 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
1708 {
1709 grub_env_set("LoadIsoEfiDriver", "on");
1710 }
1711 }
1712 else
1713 {
1714 if (ventoy_is_efi_os())
1715 {
1716 grub_env_set("LoadIsoEfiDriver", "on");
1717 }
1718 else
1719 {
1720 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
1721 }
1722 }
1723 }
1724
1725 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1726
1727 override_count = ventoy_linux_get_override_chunk_count();
1728 virt_chunk_count = ventoy_linux_get_virt_chunk_count();
1729
1730 if (ventoy_compatible)
1731 {
1732 size = sizeof(ventoy_chain_head) + img_chunk_size;
1733 }
1734 else
1735 {
1736 override_size = ventoy_linux_get_override_chunk_size();
1737 virt_chunk_size = ventoy_linux_get_virt_chunk_size();
1738 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
1739 }
1740
1741 pLastChain = grub_env_get("vtoy_chain_mem_addr");
1742 if (pLastChain)
1743 {
1744 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
1745 if (chain)
1746 {
1747 debug("free last chain memory %p\n", chain);
1748 grub_free(chain);
1749 }
1750 }
1751
1752 chain = ventoy_alloc_chain(size);
1753 if (!chain)
1754 {
1755 grub_printf("Failed to alloc chain linux memory size %u\n", size);
1756 grub_file_close(file);
1757 return 1;
1758 }
1759
1760 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
1761 grub_env_set("vtoy_chain_mem_addr", envbuf);
1762 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
1763 grub_env_set("vtoy_chain_mem_size", envbuf);
1764
1765 grub_memset(chain, 0, sizeof(ventoy_chain_head));
1766
1767 /* part 1: os parameter */
1768 g_ventoy_chain_type = ventoy_chain_linux;
1769 ventoy_fill_os_param(file, &(chain->os_param));
1770
1771 /* part 2: chain head */
1772 disk = file->device->disk;
1773 chain->disk_drive = disk->id;
1774 chain->disk_sector_size = (1 << disk->log_sector_size);
1775 chain->real_img_size_in_bytes = file->size;
1776 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
1777 chain->boot_catalog = boot_catlog;
1778
1779 if (!ventoy_is_efi_os())
1780 {
1781 grub_file_seek(file, boot_catlog * 2048);
1782 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
1783 }
1784
1785 /* part 3: image chunk */
1786 chain->img_chunk_offset = sizeof(ventoy_chain_head);
1787 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
1788 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
1789
1790 if (ventoy_compatible)
1791 {
1792 return 0;
1793 }
1794
1795 /* part 4: override chunk */
1796 if (override_count > 0)
1797 {
1798 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
1799 chain->override_chunk_num = override_count;
1800 ventoy_linux_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
1801 }
1802
1803 /* part 5: virt chunk */
1804 if (virt_chunk_count > 0)
1805 {
1806 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
1807 chain->virt_chunk_num = virt_chunk_count;
1808 ventoy_linux_fill_virt_data(isosize, chain);
1809 }
1810
1811 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1812 }
1813
1814 static char *ventoy_systemd_conf_tag(char *buf, const char *tag, int optional)
1815 {
1816 int taglen = 0;
1817 char *start = NULL;
1818 char *nextline = NULL;
1819
1820 taglen = grub_strlen(tag);
1821 for (start = buf; start; start = nextline)
1822 {
1823 nextline = ventoy_get_line(start);
1824 while (ventoy_isspace(*start))
1825 {
1826 start++;
1827 }
1828
1829 if (grub_strncmp(start, tag, taglen) == 0 && (start[taglen] == ' ' || start[taglen] == '\t'))
1830 {
1831 start += taglen;
1832 while (ventoy_isspace(*start))
1833 {
1834 start++;
1835 }
1836 return start;
1837 }
1838 }
1839
1840 if (optional == 0)
1841 {
1842 debug("tag<%s> NOT found\n", tag);
1843 }
1844 return NULL;
1845 }
1846
1847 static int ventoy_systemd_conf_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
1848 {
1849 int oldpos = 0;
1850 char *tag = NULL;
1851 char *bkbuf = NULL;
1852 char *filebuf = NULL;
1853 grub_file_t file = NULL;
1854 systemd_menu_ctx *ctx = (systemd_menu_ctx *)data;
1855
1856 debug("ventoy_systemd_conf_hook %s\n", filename);
1857
1858 if (info->dir || NULL == grub_strstr(filename, ".conf"))
1859 {
1860 return 0;
1861 }
1862
1863
1864 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s/loader/entries/%s", ctx->dev, filename);
1865 if (!file)
1866 {
1867 return 0;
1868 }
1869
1870 filebuf = grub_zalloc(2 * file->size + 8);
1871 if (!filebuf)
1872 {
1873 goto out;
1874 }
1875
1876 bkbuf = filebuf + file->size + 4;
1877 grub_file_read(file, bkbuf, file->size);
1878
1879 oldpos = ctx->pos;
1880
1881 /* title --> menuentry */
1882 grub_memcpy(filebuf, bkbuf, file->size);
1883 tag = ventoy_systemd_conf_tag(filebuf, "title", 0);
1884 vtoy_check_goto_out(tag);
1885 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, "menuentry \"%s\" {\n", tag);
1886
1887 /* linux xxx */
1888 grub_memcpy(filebuf, bkbuf, file->size);
1889 tag = ventoy_systemd_conf_tag(filebuf, "linux", 0);
1890 if (!tag)
1891 {
1892 ctx->pos = oldpos;
1893 goto out;
1894 }
1895 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, " echo \"Downloading kernel ...\"\n linux %s ", tag);
1896
1897 /* kernel options */
1898 grub_memcpy(filebuf, bkbuf, file->size);
1899 tag = ventoy_systemd_conf_tag(filebuf, "options", 0);
1900 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, "%s \n", tag ? tag : "");
1901
1902
1903 /* initrd xxx xxx xxx */
1904 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, " echo \"Downloading initrd ...\"\n initrd ");
1905 grub_memcpy(filebuf, bkbuf, file->size);
1906 tag = ventoy_systemd_conf_tag(filebuf, "initrd", 1);
1907 while (tag)
1908 {
1909 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, "%s ", tag);
1910 tag = ventoy_systemd_conf_tag(tag + grub_strlen(tag) + 1, "initrd", 1);
1911 }
1912
1913 vtoy_len_ssprintf(ctx->buf, ctx->pos, ctx->len, "\n boot\n}\n");
1914
1915 out:
1916 grub_check_free(filebuf);
1917 grub_file_close(file);
1918 return 0;
1919 }
1920
1921 grub_err_t ventoy_cmd_linux_systemd_menu(grub_extcmd_context_t ctxt, int argc, char **args)
1922 {
1923 static char *buf = NULL;
1924 char name[128];
1925 char value[64];
1926 grub_fs_t fs;
1927 char *device_name = NULL;
1928 grub_device_t dev = NULL;
1929 systemd_menu_ctx ctx;
1930
1931 (void)ctxt;
1932 (void)argc;
1933
1934 if (!buf)
1935 {
1936 buf = grub_malloc(VTOY_LINUX_SYSTEMD_MENU_MAX_BUF);
1937 if (!buf)
1938 {
1939 goto end;
1940 }
1941 }
1942
1943 device_name = grub_file_get_device_name(args[0]);
1944 if (!device_name)
1945 {
1946 debug("failed to get device name %s\n", args[0]);
1947 goto end;
1948 }
1949
1950 dev = grub_device_open(device_name);
1951 if (!dev)
1952 {
1953 debug("failed to open device %s\n", device_name);
1954 goto end;
1955 }
1956
1957 fs = grub_fs_probe(dev);
1958 if (!fs)
1959 {
1960 debug("failed to probe fs %d\n", grub_errno);
1961 goto end;
1962 }
1963
1964 ctx.dev = args[0];
1965 ctx.buf = buf;
1966 ctx.pos = 0;
1967 ctx.len = VTOY_LINUX_SYSTEMD_MENU_MAX_BUF;
1968 fs->fs_dir(dev, "/loader/entries", ventoy_systemd_conf_hook, &ctx);
1969
1970 grub_snprintf(name, sizeof(name), "%s_addr", args[1]);
1971 grub_snprintf(value, sizeof(value), "0x%llx", (ulonglong)(ulong)buf);
1972 grub_env_set(name, value);
1973
1974 grub_snprintf(name, sizeof(name), "%s_size", args[1]);
1975 grub_snprintf(value, sizeof(value), "%d", ctx.pos);
1976 grub_env_set(name, value);
1977
1978 end:
1979 grub_check_free(device_name);
1980 check_free(dev, grub_device_close);
1981 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1982 }
1983