]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_linux.c
29c368bb10aef07eb9ade53687944fb203e13106
[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 char * ventoy_get_line(char *start)
42 {
43 if (start == NULL)
44 {
45 return NULL;
46 }
47
48 while (*start && *start != '\n')
49 {
50 start++;
51 }
52
53 if (*start == 0)
54 {
55 return NULL;
56 }
57 else
58 {
59 *start = 0;
60 return start + 1;
61 }
62 }
63
64 static initrd_info * ventoy_find_initrd_by_name(initrd_info *list, const char *name)
65 {
66 initrd_info *node = list;
67
68 while (node)
69 {
70 if (grub_strcmp(node->name, name) == 0)
71 {
72 return node;
73 }
74 node = node->next;
75 }
76
77 return NULL;
78 }
79
80 grub_err_t ventoy_cmd_clear_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args)
81 {
82 initrd_info *node = g_initrd_img_list;
83 initrd_info *next;
84
85 (void)ctxt;
86 (void)argc;
87 (void)args;
88
89 while (node)
90 {
91 next = node->next;
92 grub_free(node);
93 node = next;
94 }
95
96 g_initrd_img_list = NULL;
97 g_initrd_img_tail = NULL;
98 g_initrd_img_count = 0;
99 g_valid_initrd_count = 0;
100
101 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
102 }
103
104 grub_err_t ventoy_cmd_dump_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args)
105 {
106 int i = 0;
107 initrd_info *node = g_initrd_img_list;
108
109 (void)ctxt;
110 (void)argc;
111 (void)args;
112
113 grub_printf("###################\n");
114 grub_printf("initrd info list: valid count:%d\n", g_valid_initrd_count);
115
116 while (node)
117 {
118 grub_printf("%s ", node->size > 0 ? "*" : " ");
119 grub_printf("%02u %s offset:%llu size:%llu \n", i++, node->name, (unsigned long long)node->offset,
120 (unsigned long long)node->size);
121 node = node->next;
122 }
123
124 grub_printf("###################\n");
125
126 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
127 }
128
129 static void ventoy_parse_directory(char *path, char *dir, int buflen)
130 {
131 int end;
132 char *pos;
133
134 pos = grub_strstr(path, ")");
135 if (!pos)
136 {
137 pos = path;
138 }
139
140 end = grub_snprintf(dir, buflen, "%s", pos + 1);
141 while (end > 0)
142 {
143 if (dir[end] == '/')
144 {
145 dir[end + 1] = 0;
146 break;
147 }
148 end--;
149 }
150 }
151
152 static grub_err_t ventoy_isolinux_initrd_collect(grub_file_t file, const char *prefix)
153 {
154 int i = 0;
155 int offset;
156 int prefixlen = 0;
157 char *buf = NULL;
158 char *pos = NULL;
159 char *start = NULL;
160 char *nextline = NULL;
161 initrd_info *img = NULL;
162
163 prefixlen = grub_strlen(prefix);
164
165 buf = grub_zalloc(file->size + 2);
166 if (!buf)
167 {
168 return 0;
169 }
170
171 grub_file_read(file, buf, file->size);
172
173 for (start = buf; start; start = nextline)
174 {
175 nextline = ventoy_get_line(start);
176
177 while (ventoy_isspace(*start))
178 {
179 start++;
180 }
181
182 offset = 7; // strlen("initrd=") or "INITRD " or "initrd "
183 pos = grub_strstr(start, "initrd=");
184 if (pos == NULL)
185 {
186 pos = start;
187
188 if (grub_strncmp(start, "INITRD", 6) != 0 && grub_strncmp(start, "initrd", 6) != 0)
189 {
190 if (grub_strstr(start, "xen") &&
191 ((pos = grub_strstr(start, "--- /install.img")) != NULL ||
192 (pos = grub_strstr(start, "--- initrd.img")) != NULL
193 ))
194 {
195 offset = 4; // "--- "
196 }
197 else
198 {
199 continue;
200 }
201 }
202 }
203
204 pos += offset;
205
206 while (1)
207 {
208 i = 0;
209 img = grub_zalloc(sizeof(initrd_info));
210 if (!img)
211 {
212 break;
213 }
214
215 if (*pos != '/')
216 {
217 grub_strcpy(img->name, prefix);
218 i = prefixlen;
219 }
220
221 while (i < 255 && (0 == ventoy_is_word_end(*pos)))
222 {
223 img->name[i++] = *pos++;
224 }
225
226 if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
227 {
228 grub_free(img);
229 }
230 else
231 {
232 if (g_initrd_img_list)
233 {
234 img->prev = g_initrd_img_tail;
235 g_initrd_img_tail->next = img;
236 }
237 else
238 {
239 g_initrd_img_list = img;
240 }
241
242 g_initrd_img_tail = img;
243 g_initrd_img_count++;
244 }
245
246 if (*pos == ',')
247 {
248 pos++;
249 }
250 else
251 {
252 break;
253 }
254 }
255 }
256
257 grub_free(buf);
258 return GRUB_ERR_NONE;
259 }
260
261 static int ventoy_isolinux_initrd_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
262 {
263 grub_file_t file = NULL;
264 ventoy_initrd_ctx *ctx = (ventoy_initrd_ctx *)data;
265
266 (void)info;
267
268 if (NULL == grub_strstr(filename, ".cfg") && NULL == grub_strstr(filename, ".CFG"))
269 {
270 return 0;
271 }
272
273 debug("init hook dir <%s%s>\n", ctx->path_prefix, filename);
274
275 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", ctx->path_prefix, filename);
276 if (!file)
277 {
278 return 0;
279 }
280
281 ventoy_isolinux_initrd_collect(file, ctx->dir_prefix);
282 grub_file_close(file);
283
284 return 0;
285 }
286
287 grub_err_t ventoy_cmd_isolinux_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args)
288 {
289 grub_fs_t fs;
290 grub_device_t dev = NULL;
291 char *device_name = NULL;
292 ventoy_initrd_ctx ctx;
293 char directory[256];
294
295 (void)ctxt;
296 (void)argc;
297
298 device_name = grub_file_get_device_name(args[0]);
299 if (!device_name)
300 {
301 goto end;
302 }
303
304 dev = grub_device_open(device_name);
305 if (!dev)
306 {
307 goto end;
308 }
309
310 fs = grub_fs_probe(dev);
311 if (!fs)
312 {
313 goto end;
314 }
315
316 debug("isolinux initrd collect %s\n", args[0]);
317
318 ventoy_parse_directory(args[0], directory, sizeof(directory) - 1);
319 ctx.path_prefix = args[0];
320 ctx.dir_prefix = (argc > 1) ? args[1] : directory;
321
322 debug("path_prefix=<%s> dir_prefix=<%s>\n", ctx.path_prefix, ctx.dir_prefix);
323
324 fs->fs_dir(dev, directory, ventoy_isolinux_initrd_hook, &ctx);
325
326 end:
327 check_free(device_name, grub_free);
328 check_free(dev, grub_device_close);
329
330 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
331 }
332
333 static grub_err_t ventoy_grub_cfg_initrd_collect(const char *fileName)
334 {
335 int i = 0;
336 grub_file_t file = NULL;
337 char *buf = NULL;
338 char *start = NULL;
339 char *nextline = NULL;
340 initrd_info *img = NULL;
341
342 debug("grub initrd collect %s\n", fileName);
343
344 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", fileName);
345 if (!file)
346 {
347 return 0;
348 }
349
350 buf = grub_zalloc(file->size + 2);
351 if (!buf)
352 {
353 grub_file_close(file);
354 return 0;
355 }
356
357 grub_file_read(file, buf, file->size);
358
359 for (start = buf; start; start = nextline)
360 {
361 nextline = ventoy_get_line(start);
362
363 while (ventoy_isspace(*start))
364 {
365 start++;
366 }
367
368 if (grub_strncmp(start, "initrd", 6) != 0)
369 {
370 continue;
371 }
372
373 start += 6;
374 while (*start && (!ventoy_isspace(*start)))
375 {
376 start++;
377 }
378
379 while (ventoy_isspace(*start))
380 {
381 start++;
382 }
383
384 while (*start)
385 {
386 img = grub_zalloc(sizeof(initrd_info));
387 if (!img)
388 {
389 break;
390 }
391
392 for (i = 0; i < 255 && (0 == ventoy_is_word_end(*start)); i++)
393 {
394 img->name[i] = *start++;
395 }
396
397 if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
398 {
399 grub_free(img);
400 }
401 else
402 {
403 if (g_initrd_img_list)
404 {
405 img->prev = g_initrd_img_tail;
406 g_initrd_img_tail->next = img;
407 }
408 else
409 {
410 g_initrd_img_list = img;
411 }
412
413 g_initrd_img_tail = img;
414 g_initrd_img_count++;
415 }
416
417 if (*start == ' ' || *start == '\t')
418 {
419 while (ventoy_isspace(*start))
420 {
421 start++;
422 }
423 }
424 else
425 {
426 break;
427 }
428 }
429 }
430
431 grub_free(buf);
432 grub_file_close(file);
433
434 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
435 }
436
437 static int ventoy_grub_initrd_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
438 {
439 char filePath[256];
440 ventoy_initrd_ctx *ctx = (ventoy_initrd_ctx *)data;
441
442 (void)info;
443
444 debug("ventoy_grub_initrd_hook %s\n", filename);
445
446 if (NULL == grub_strstr(filename, ".cfg") &&
447 NULL == grub_strstr(filename, ".CFG") &&
448 NULL == grub_strstr(filename, ".conf"))
449 {
450 return 0;
451 }
452
453 debug("init hook dir <%s%s>\n", ctx->path_prefix, filename);
454
455 grub_snprintf(filePath, sizeof(filePath) - 1, "%s%s", ctx->dir_prefix, filename);
456 ventoy_grub_cfg_initrd_collect(filePath);
457
458 return 0;
459 }
460
461 grub_err_t ventoy_cmd_grub_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args)
462 {
463 grub_fs_t fs;
464 grub_device_t dev = NULL;
465 char *device_name = NULL;
466 ventoy_initrd_ctx ctx;
467
468 (void)ctxt;
469 (void)argc;
470
471 if (argc != 2)
472 {
473 return 0;
474 }
475
476 debug("grub initrd collect %s %s\n", args[0], args[1]);
477
478 if (grub_strcmp(args[0], "file") == 0)
479 {
480 return ventoy_grub_cfg_initrd_collect(args[1]);
481 }
482
483 device_name = grub_file_get_device_name(args[1]);
484 if (!device_name)
485 {
486 debug("failed to get device name %s\n", args[1]);
487 goto end;
488 }
489
490 dev = grub_device_open(device_name);
491 if (!dev)
492 {
493 debug("failed to open device %s\n", device_name);
494 goto end;
495 }
496
497 fs = grub_fs_probe(dev);
498 if (!fs)
499 {
500 debug("failed to probe fs %d\n", grub_errno);
501 goto end;
502 }
503
504 ctx.dir_prefix = args[1];
505 ctx.path_prefix = grub_strstr(args[1], device_name);
506 if (ctx.path_prefix)
507 {
508 ctx.path_prefix += grub_strlen(device_name) + 1;
509 }
510 else
511 {
512 ctx.path_prefix = args[1];
513 }
514
515 debug("ctx.path_prefix:<%s>\n", ctx.path_prefix);
516
517 fs->fs_dir(dev, ctx.path_prefix, ventoy_grub_initrd_hook, &ctx);
518
519 end:
520 check_free(device_name, grub_free);
521 check_free(dev, grub_device_close);
522
523
524 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
525 }
526
527 grub_err_t ventoy_cmd_specify_initrd_file(grub_extcmd_context_t ctxt, int argc, char **args)
528 {
529 initrd_info *img = NULL;
530
531 (void)ctxt;
532 (void)argc;
533
534 debug("ventoy_cmd_specify_initrd_file %s\n", args[0]);
535
536 img = grub_zalloc(sizeof(initrd_info));
537 if (!img)
538 {
539 return 1;
540 }
541
542 grub_strncpy(img->name, args[0], sizeof(img->name));
543 if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
544 {
545 debug("%s is already exist\n", args[0]);
546 grub_free(img);
547 }
548 else
549 {
550 if (g_initrd_img_list)
551 {
552 img->prev = g_initrd_img_tail;
553 g_initrd_img_tail->next = img;
554 }
555 else
556 {
557 g_initrd_img_list = img;
558 }
559
560 g_initrd_img_tail = img;
561 g_initrd_img_count++;
562 }
563
564 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
565 }
566
567 static int ventoy_cpio_newc_get_int(char *value)
568 {
569 char buf[16] = {0};
570
571 grub_memcpy(buf, value, 8);
572 return (int)grub_strtoul(buf, NULL, 16);
573 }
574
575 static void ventoy_cpio_newc_fill_int(grub_uint32_t value, char *buf, int buflen)
576 {
577 int i;
578 int len;
579 char intbuf[32];
580
581 len = grub_snprintf(intbuf, sizeof(intbuf), "%x", value);
582
583 for (i = 0; i < buflen; i++)
584 {
585 buf[i] = '0';
586 }
587
588 if (len > buflen)
589 {
590 grub_printf("int buf len overflow %d %d\n", len, buflen);
591 }
592 else
593 {
594 grub_memcpy(buf + buflen - len, intbuf, len);
595 }
596 }
597
598 int ventoy_cpio_newc_fill_head(void *buf, int filesize, const void *filedata, const char *name)
599 {
600 int namelen = 0;
601 int headlen = 0;
602 static grub_uint32_t cpio_ino = 0xFFFFFFF0;
603 cpio_newc_header *cpio = (cpio_newc_header *)buf;
604
605 namelen = grub_strlen(name) + 1;
606 headlen = sizeof(cpio_newc_header) + namelen;
607 headlen = ventoy_align(headlen, 4);
608
609 grub_memset(cpio, '0', sizeof(cpio_newc_header));
610 grub_memset(cpio + 1, 0, headlen - sizeof(cpio_newc_header));
611
612 grub_memcpy(cpio->c_magic, "070701", 6);
613 ventoy_cpio_newc_fill_int(cpio_ino--, cpio->c_ino, 8);
614 ventoy_cpio_newc_fill_int(0100777, cpio->c_mode, 8);
615 ventoy_cpio_newc_fill_int(1, cpio->c_nlink, 8);
616 ventoy_cpio_newc_fill_int(filesize, cpio->c_filesize, 8);
617 ventoy_cpio_newc_fill_int(namelen, cpio->c_namesize, 8);
618 grub_memcpy(cpio + 1, name, namelen);
619
620 if (filedata)
621 {
622 grub_memcpy((char *)cpio + headlen, filedata, filesize);
623 }
624
625 return headlen;
626 }
627
628 static grub_uint32_t ventoy_linux_get_virt_chunk_size(void)
629 {
630 return (sizeof(ventoy_virt_chunk) + g_ventoy_cpio_size) * g_valid_initrd_count;
631 }
632
633 static void ventoy_linux_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
634 {
635 int id = 0;
636 initrd_info *node;
637 grub_uint64_t sector;
638 grub_uint32_t offset;
639 grub_uint32_t cpio_secs;
640 grub_uint32_t initrd_secs;
641 char *override;
642 ventoy_virt_chunk *cur;
643 char name[32];
644
645 override = (char *)chain + chain->virt_chunk_offset;
646 sector = (isosize + 2047) / 2048;
647 cpio_secs = g_ventoy_cpio_size / 2048;
648
649 offset = g_valid_initrd_count * sizeof(ventoy_virt_chunk);
650 cur = (ventoy_virt_chunk *)override;
651
652 for (node = g_initrd_img_list; node; node = node->next)
653 {
654 if (node->size == 0)
655 {
656 continue;
657 }
658
659 initrd_secs = (grub_uint32_t)((node->size + 2047) / 2048);
660
661 cur->mem_sector_start = sector;
662 cur->mem_sector_end = cur->mem_sector_start + cpio_secs;
663 cur->mem_sector_offset = offset;
664 cur->remap_sector_start = cur->mem_sector_end;
665 cur->remap_sector_end = cur->remap_sector_start + initrd_secs;
666 cur->org_sector_start = (grub_uint32_t)(node->offset / 2048);
667
668 grub_memcpy(g_ventoy_runtime_buf, &chain->os_param, sizeof(ventoy_os_param));
669
670 grub_memset(name, 0, 16);
671 grub_snprintf(name, sizeof(name), "initrd%03d", ++id);
672
673 grub_memcpy(g_ventoy_initrd_head + 1, name, 16);
674 ventoy_cpio_newc_fill_int((grub_uint32_t)node->size, g_ventoy_initrd_head->c_filesize, 8);
675
676 grub_memcpy(override + offset, g_ventoy_cpio_buf, g_ventoy_cpio_size);
677
678 chain->virt_img_size_in_bytes += g_ventoy_cpio_size + initrd_secs * 2048;
679
680 offset += g_ventoy_cpio_size;
681 sector += cpio_secs + initrd_secs;
682 cur++;
683 }
684
685 return;
686 }
687
688 static grub_uint32_t ventoy_linux_get_override_chunk_size(void)
689 {
690 return sizeof(ventoy_override_chunk) * g_valid_initrd_count;
691 }
692
693 static void ventoy_linux_fill_override_data( grub_uint64_t isosize, void *override)
694 {
695 initrd_info *node;
696 grub_uint32_t mod;
697 grub_uint32_t newlen;
698 grub_uint64_t sector;
699 ventoy_override_chunk *cur;
700
701 sector = (isosize + 2047) / 2048;
702
703 cur = (ventoy_override_chunk *)override;
704 for (node = g_initrd_img_list; node; node = node->next)
705 {
706 if (node->size == 0)
707 {
708 continue;
709 }
710
711 newlen = (grub_uint32_t)(node->size + g_ventoy_cpio_size);
712 mod = newlen % 4;
713 if (mod > 0)
714 {
715 newlen += 4 - mod;
716 }
717
718 if (node->iso_type == 0)
719 {
720 ventoy_iso9660_override *dirent = (ventoy_iso9660_override *)node->override_data;
721
722 node->override_length = sizeof(ventoy_iso9660_override);
723 dirent->first_sector = (grub_uint32_t)sector;
724 dirent->size = newlen;
725 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
726 dirent->size_be = grub_swap_bytes32(dirent->size);
727
728 sector += (dirent->size + 2047) / 2048;
729 }
730 else
731 {
732 ventoy_udf_override *udf = (ventoy_udf_override *)node->override_data;
733
734 node->override_length = sizeof(ventoy_udf_override);
735 udf->length = newlen;
736 udf->position = (grub_uint32_t)sector - node->udf_start_block;
737
738 sector += (udf->length + 2047) / 2048;
739 }
740
741 cur->img_offset = node->override_offset;
742 cur->override_size = node->override_length;
743 grub_memcpy(cur->override_data, node->override_data, cur->override_size);
744 cur++;
745 }
746
747 return;
748 }
749
750 grub_err_t ventoy_cmd_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args)
751 {
752 char buf[32] = {0};
753
754 (void)ctxt;
755 (void)argc;
756 (void)args;
757
758 if (argc == 1)
759 {
760 grub_snprintf(buf, sizeof(buf), "%d", g_initrd_img_count);
761 grub_env_set(args[0], buf);
762 }
763
764 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
765 }
766
767 grub_err_t ventoy_cmd_valid_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args)
768 {
769 char buf[32] = {0};
770
771 (void)ctxt;
772 (void)argc;
773 (void)args;
774
775 if (argc == 1)
776 {
777 grub_snprintf(buf, sizeof(buf), "%d", g_valid_initrd_count);
778 grub_env_set(args[0], buf);
779 }
780
781 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
782 }
783
784 static grub_err_t ventoy_linux_locate_initrd(int filt, int *filtcnt)
785 {
786 int data;
787 int filtbysize = 1;
788 int sizefilt = 0;
789 grub_file_t file;
790 initrd_info *node;
791
792 debug("ventoy_linux_locate_initrd %d\n", filt);
793
794 g_valid_initrd_count = 0;
795
796 if (grub_env_get("INITRD_NO_SIZE_FILT"))
797 {
798 filtbysize = 0;
799 }
800
801 for (node = g_initrd_img_list; node; node = node->next)
802 {
803 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(loop)%s", node->name);
804 if (!file)
805 {
806 continue;
807 }
808
809 debug("file <%s> size:%d\n", node->name, (int)file->size);
810
811 /* initrd file too small */
812 if (filtbysize
813 && (NULL == grub_strstr(node->name, "minirt.gz"))
814 && (NULL == grub_strstr(node->name, "initrd.xz"))
815 )
816 {
817 if (filt > 0 && file->size <= g_ventoy_cpio_size + 2048)
818 {
819 debug("file size too small %d\n", (int)g_ventoy_cpio_size);
820 grub_file_close(file);
821 sizefilt++;
822 continue;
823 }
824 }
825
826 if (grub_strcmp(file->fs->name, "iso9660") == 0)
827 {
828 node->iso_type = 0;
829 node->override_offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
830
831 grub_file_read(file, &data, 1); // just read for hook trigger
832 node->offset = grub_iso9660_get_last_read_pos(file);
833 }
834 else
835 {
836 /* TBD */
837 }
838
839 node->size = file->size;
840 g_valid_initrd_count++;
841
842 grub_file_close(file);
843 }
844
845 *filtcnt = sizefilt;
846
847 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
848 }
849
850
851 grub_err_t ventoy_cmd_linux_get_main_initrd_index(grub_extcmd_context_t ctxt, int argc, char **args)
852 {
853 int index = 0;
854 char buf[32];
855 initrd_info *node = NULL;
856
857 (void)ctxt;
858 (void)argc;
859 (void)args;
860
861 if (argc != 1)
862 {
863 return 1;
864 }
865
866 if (g_initrd_img_count == 1)
867 {
868 ventoy_set_env(args[0], "0");
869 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
870 }
871
872 for (node = g_initrd_img_list; node; node = node->next)
873 {
874 if (node->size <= 0)
875 {
876 continue;
877 }
878
879 if (grub_strstr(node->name, "ucode") || grub_strstr(node->name, "-firmware"))
880 {
881 index++;
882 continue;
883 }
884
885 grub_snprintf(buf, sizeof(buf), "%d", index);
886 ventoy_set_env(args[0], buf);
887 break;
888 }
889
890 debug("main initrd index:%d\n", index);
891
892 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
893 }
894
895 grub_err_t ventoy_cmd_linux_locate_initrd(grub_extcmd_context_t ctxt, int argc, char **args)
896 {
897 int sizefilt = 0;
898
899 (void)ctxt;
900 (void)argc;
901 (void)args;
902
903 ventoy_linux_locate_initrd(1, &sizefilt);
904
905 if (g_valid_initrd_count == 0 && sizefilt > 0)
906 {
907 ventoy_linux_locate_initrd(0, &sizefilt);
908 }
909
910 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
911 }
912
913 static int ventoy_cpio_busybox64(cpio_newc_header *head)
914 {
915 char *name;
916 int namelen;
917 int offset;
918 int count = 0;
919
920 name = (char *)(head + 1);
921 while (name[0] && count < 2)
922 {
923 if (grub_strcmp(name, "ventoy/busybox/ash") == 0)
924 {
925 grub_memcpy(name, "ventoy/busybox/32h", 18);
926 count++;
927 }
928 else if (grub_strcmp(name, "ventoy/busybox/64h") == 0)
929 {
930 grub_memcpy(name, "ventoy/busybox/ash", 18);
931 count++;
932 }
933
934 namelen = ventoy_cpio_newc_get_int(head->c_namesize);
935 offset = sizeof(cpio_newc_header) + namelen;
936 offset = ventoy_align(offset, 4);
937 offset += ventoy_cpio_newc_get_int(head->c_filesize);
938 offset = ventoy_align(offset, 4);
939
940 head = (cpio_newc_header *)((char *)head + offset);
941 name = (char *)(head + 1);
942 }
943
944 return 0;
945 }
946
947
948 grub_err_t ventoy_cmd_cpio_busybox_64(grub_extcmd_context_t ctxt, int argc, char **args)
949 {
950 (void)ctxt;
951 (void)argc;
952 (void)args;
953
954 debug("ventoy_cmd_busybox_64 %d\n", argc);
955 ventoy_cpio_busybox64((cpio_newc_header *)g_ventoy_cpio_buf);
956 return 0;
957 }
958
959
960 grub_err_t ventoy_cmd_load_cpio(grub_extcmd_context_t ctxt, int argc, char **args)
961 {
962 int rc;
963 char *template_file = NULL;
964 char *template_buf = NULL;
965 char *persistent_buf = NULL;
966 char *injection_buf = NULL;
967 const char *injection_file = NULL;
968 grub_uint8_t *buf = NULL;
969 grub_uint32_t mod;
970 grub_uint32_t headlen;
971 grub_uint32_t initrd_head_len;
972 grub_uint32_t padlen;
973 grub_uint32_t img_chunk_size;
974 grub_uint32_t template_size = 0;
975 grub_uint32_t persistent_size = 0;
976 grub_uint32_t injection_size = 0;
977 grub_file_t file;
978 grub_file_t tmpfile;
979 ventoy_img_chunk_list chunk_list;
980
981 (void)ctxt;
982 (void)argc;
983
984 if (argc != 4)
985 {
986 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s cpiofile\n", cmd_raw_name);
987 }
988
989 if (g_img_chunk_list.chunk == NULL || g_img_chunk_list.cur_chunk == 0)
990 {
991 return grub_error(GRUB_ERR_BAD_ARGUMENT, "image chunk is null\n");
992 }
993
994 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
995
996 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
997 if (!file)
998 {
999 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]);
1000 }
1001
1002 if (g_ventoy_cpio_buf)
1003 {
1004 grub_free(g_ventoy_cpio_buf);
1005 g_ventoy_cpio_buf = NULL;
1006 g_ventoy_cpio_size = 0;
1007 }
1008
1009 rc = ventoy_plugin_get_persistent_chunklist(args[1], -1, &chunk_list);
1010 if (rc == 0 && chunk_list.cur_chunk > 0 && chunk_list.chunk)
1011 {
1012 persistent_size = chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1013 persistent_buf = (char *)(chunk_list.chunk);
1014 }
1015
1016 template_file = ventoy_plugin_get_cur_install_template(args[1]);
1017 if (template_file)
1018 {
1019 debug("auto install template: <%s>\n", template_file);
1020 tmpfile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", args[2], template_file);
1021 if (tmpfile)
1022 {
1023 debug("auto install script size %d\n", (int)tmpfile->size);
1024 template_size = tmpfile->size;
1025 template_buf = grub_malloc(template_size);
1026 if (template_buf)
1027 {
1028 grub_file_read(tmpfile, template_buf, template_size);
1029 }
1030
1031 grub_file_close(tmpfile);
1032 }
1033 else
1034 {
1035 debug("Failed to open install script %s%s\n", args[2], template_file);
1036 }
1037 }
1038 else
1039 {
1040 debug("auto install script skipped or not configed %s\n", args[1]);
1041 }
1042
1043 injection_file = ventoy_plugin_get_injection(args[1]);
1044 if (injection_file)
1045 {
1046 debug("injection archive: <%s>\n", injection_file);
1047 tmpfile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", args[2], injection_file);
1048 if (tmpfile)
1049 {
1050 debug("injection archive size:%d\n", (int)tmpfile->size);
1051 injection_size = tmpfile->size;
1052 injection_buf = grub_malloc(injection_size);
1053 if (injection_buf)
1054 {
1055 grub_file_read(tmpfile, injection_buf, injection_size);
1056 }
1057
1058 grub_file_close(tmpfile);
1059 }
1060 else
1061 {
1062 debug("Failed to open injection archive %s%s\n", args[2], injection_file);
1063 }
1064 }
1065 else
1066 {
1067 debug("injection not configed %s\n", args[1]);
1068 }
1069
1070 g_ventoy_cpio_buf = grub_malloc(file->size + 4096 + template_size + persistent_size + injection_size + img_chunk_size);
1071 if (NULL == g_ventoy_cpio_buf)
1072 {
1073 grub_file_close(file);
1074 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't alloc memory %llu\n", file->size + 4096 + img_chunk_size);
1075 }
1076
1077 grub_file_read(file, g_ventoy_cpio_buf, file->size);
1078
1079 buf = (grub_uint8_t *)(g_ventoy_cpio_buf + file->size - 4);
1080 while (*((grub_uint32_t *)buf) != 0x37303730)
1081 {
1082 buf -= 4;
1083 }
1084
1085 /* get initrd head len */
1086 initrd_head_len = ventoy_cpio_newc_fill_head(buf, 0, NULL, "initrd000.xx");
1087
1088 /* step1: insert image chunk data to cpio */
1089 headlen = ventoy_cpio_newc_fill_head(buf, img_chunk_size, g_img_chunk_list.chunk, "ventoy/ventoy_image_map");
1090 buf += headlen + ventoy_align(img_chunk_size, 4);
1091
1092 if (template_buf)
1093 {
1094 headlen = ventoy_cpio_newc_fill_head(buf, template_size, template_buf, "ventoy/autoinstall");
1095 buf += headlen + ventoy_align(template_size, 4);
1096 }
1097
1098 if (persistent_size > 0 && persistent_buf)
1099 {
1100 headlen = ventoy_cpio_newc_fill_head(buf, persistent_size, persistent_buf, "ventoy/ventoy_persistent_map");
1101 buf += headlen + ventoy_align(persistent_size, 4);
1102
1103 grub_free(persistent_buf);
1104 persistent_buf = NULL;
1105 }
1106
1107 if (injection_size > 0 && injection_buf)
1108 {
1109 headlen = ventoy_cpio_newc_fill_head(buf, injection_size, injection_buf, "ventoy/ventoy_injection");
1110 buf += headlen + ventoy_align(injection_size, 4);
1111
1112 grub_free(injection_buf);
1113 injection_buf = NULL;
1114 }
1115
1116 /* step2: insert os param to cpio */
1117 headlen = ventoy_cpio_newc_fill_head(buf, 0, NULL, "ventoy/ventoy_os_param");
1118 padlen = sizeof(ventoy_os_param);
1119 g_ventoy_cpio_size = (grub_uint32_t)(buf - g_ventoy_cpio_buf) + headlen + padlen + initrd_head_len;
1120 mod = g_ventoy_cpio_size % 2048;
1121 if (mod)
1122 {
1123 g_ventoy_cpio_size += 2048 - mod;
1124 padlen += 2048 - mod;
1125 }
1126
1127 /* update os param data size, the data will be updated before chain boot */
1128 ventoy_cpio_newc_fill_int(padlen, ((cpio_newc_header *)buf)->c_filesize, 8);
1129 g_ventoy_runtime_buf = (grub_uint8_t *)buf + headlen;
1130
1131 /* step3: fill initrd cpio head, the file size will be updated before chain boot */
1132 g_ventoy_initrd_head = (cpio_newc_header *)(g_ventoy_runtime_buf + padlen);
1133 ventoy_cpio_newc_fill_head(g_ventoy_initrd_head, 0, NULL, "initrd000.xx");
1134
1135 grub_file_close(file);
1136
1137 if (grub_strcmp(args[3], "busybox=64") == 0)
1138 {
1139 debug("cpio busybox proc %s\n", args[3]);
1140 ventoy_cpio_busybox64((cpio_newc_header *)g_ventoy_cpio_buf);
1141 }
1142
1143 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1144 }
1145
1146 grub_err_t ventoy_cmd_trailer_cpio(grub_extcmd_context_t ctxt, int argc, char **args)
1147 {
1148 int mod;
1149 int bufsize;
1150 int namelen;
1151 int offset;
1152 char *name;
1153 grub_uint8_t *bufend;
1154 cpio_newc_header *head;
1155 grub_file_t file;
1156 char value[64];
1157 const grub_uint8_t trailler[124] = {
1158 0x30, 0x37, 0x30, 0x37, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1159 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1160 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30,
1161 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1162 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1163 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
1164 0x30, 0x30, 0x30, 0x30, 0x30, 0x42, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x54, 0x52,
1165 0x41, 0x49, 0x4C, 0x45, 0x52, 0x21, 0x21, 0x21, 0x00, 0x00, 0x00, 0x00
1166 };
1167
1168 (void)ctxt;
1169 (void)argc;
1170
1171 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", args[0], args[1]);
1172 if (!file)
1173 {
1174 return 1;
1175 }
1176
1177 grub_memset(g_ventoy_runtime_buf, 0, sizeof(ventoy_os_param));
1178 ventoy_fill_os_param(file, (ventoy_os_param *)g_ventoy_runtime_buf);
1179
1180 grub_file_close(file);
1181
1182 grub_memcpy(g_ventoy_initrd_head, trailler, sizeof(trailler));
1183 bufend = (grub_uint8_t *)g_ventoy_initrd_head + sizeof(trailler);
1184
1185 bufsize = (int)(bufend - g_ventoy_cpio_buf);
1186 mod = bufsize % 512;
1187 if (mod)
1188 {
1189 grub_memset(bufend, 0, 512 - mod);
1190 bufsize += 512 - mod;
1191 }
1192
1193 if (argc > 1 && grub_strcmp(args[2], "noinit") == 0)
1194 {
1195 head = (cpio_newc_header *)g_ventoy_cpio_buf;
1196 name = (char *)(head + 1);
1197
1198 while (grub_strcmp(name, "TRAILER!!!"))
1199 {
1200 if (grub_strcmp(name, "init") == 0)
1201 {
1202 grub_memcpy(name, "xxxx", 4);
1203 }
1204 else if (grub_strcmp(name, "linuxrc") == 0)
1205 {
1206 grub_memcpy(name, "vtoyxrc", 7);
1207 }
1208 else if (grub_strcmp(name, "sbin") == 0)
1209 {
1210 grub_memcpy(name, "vtoy", 4);
1211 }
1212 else if (grub_strcmp(name, "sbin/init") == 0)
1213 {
1214 grub_memcpy(name, "vtoy/vtoy", 9);
1215 }
1216
1217 namelen = ventoy_cpio_newc_get_int(head->c_namesize);
1218 offset = sizeof(cpio_newc_header) + namelen;
1219 offset = ventoy_align(offset, 4);
1220 offset += ventoy_cpio_newc_get_int(head->c_filesize);
1221 offset = ventoy_align(offset, 4);
1222
1223 head = (cpio_newc_header *)((char *)head + offset);
1224 name = (char *)(head + 1);
1225 }
1226 }
1227
1228 grub_snprintf(value, sizeof(value), "0x%llx", (ulonglong)(ulong)g_ventoy_cpio_buf);
1229 ventoy_set_env("ventoy_cpio_addr", value);
1230 grub_snprintf(value, sizeof(value), "%d", bufsize);
1231 ventoy_set_env("ventoy_cpio_size", value);
1232
1233 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1234 }
1235
1236
1237 grub_err_t ventoy_cmd_linux_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
1238 {
1239 int ventoy_compatible = 0;
1240 grub_uint32_t size = 0;
1241 grub_uint64_t isosize = 0;
1242 grub_uint32_t boot_catlog = 0;
1243 grub_uint32_t img_chunk_size = 0;
1244 grub_uint32_t override_size = 0;
1245 grub_uint32_t virt_chunk_size = 0;
1246 grub_file_t file;
1247 grub_disk_t disk;
1248 const char *pLastChain = NULL;
1249 const char *compatible;
1250 ventoy_chain_head *chain;
1251 char envbuf[64];
1252
1253 (void)ctxt;
1254 (void)argc;
1255
1256 compatible = grub_env_get("ventoy_compatible");
1257 if (compatible && compatible[0] == 'Y')
1258 {
1259 ventoy_compatible = 1;
1260 }
1261
1262 if ((NULL == g_img_chunk_list.chunk) || (0 == ventoy_compatible && g_ventoy_cpio_buf == NULL))
1263 {
1264 grub_printf("ventoy not ready\n");
1265 return 1;
1266 }
1267
1268 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
1269 if (!file)
1270 {
1271 return 1;
1272 }
1273
1274 isosize = file->size;
1275
1276 boot_catlog = ventoy_get_iso_boot_catlog(file);
1277 if (boot_catlog)
1278 {
1279 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
1280 {
1281 grub_env_set("LoadIsoEfiDriver", "on");
1282 }
1283 }
1284 else
1285 {
1286 if (ventoy_is_efi_os())
1287 {
1288 grub_env_set("LoadIsoEfiDriver", "on");
1289 }
1290 else
1291 {
1292 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
1293 }
1294 }
1295
1296 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1297
1298 if (ventoy_compatible)
1299 {
1300 size = sizeof(ventoy_chain_head) + img_chunk_size;
1301 }
1302 else
1303 {
1304 override_size = ventoy_linux_get_override_chunk_size();
1305 virt_chunk_size = ventoy_linux_get_virt_chunk_size();
1306 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
1307 }
1308
1309 pLastChain = grub_env_get("vtoy_chain_mem_addr");
1310 if (pLastChain)
1311 {
1312 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
1313 if (chain)
1314 {
1315 debug("free last chain memory %p\n", chain);
1316 grub_free(chain);
1317 }
1318 }
1319
1320 chain = grub_malloc(size);
1321 if (!chain)
1322 {
1323 grub_printf("Failed to alloc chain memory size %u\n", size);
1324 grub_file_close(file);
1325 return 1;
1326 }
1327
1328 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
1329 grub_env_set("vtoy_chain_mem_addr", envbuf);
1330 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
1331 grub_env_set("vtoy_chain_mem_size", envbuf);
1332
1333 grub_memset(chain, 0, sizeof(ventoy_chain_head));
1334
1335 /* part 1: os parameter */
1336 g_ventoy_chain_type = ventoy_chain_linux;
1337 ventoy_fill_os_param(file, &(chain->os_param));
1338
1339 /* part 2: chain head */
1340 disk = file->device->disk;
1341 chain->disk_drive = disk->id;
1342 chain->disk_sector_size = (1 << disk->log_sector_size);
1343 chain->real_img_size_in_bytes = file->size;
1344 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
1345 chain->boot_catalog = boot_catlog;
1346
1347 if (!ventoy_is_efi_os())
1348 {
1349 grub_file_seek(file, boot_catlog * 2048);
1350 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
1351 }
1352
1353 /* part 3: image chunk */
1354 chain->img_chunk_offset = sizeof(ventoy_chain_head);
1355 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
1356 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
1357
1358 if (ventoy_compatible)
1359 {
1360 return 0;
1361 }
1362
1363 if (g_valid_initrd_count == 0)
1364 {
1365 return 0;
1366 }
1367
1368 /* part 4: override chunk */
1369 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
1370 chain->override_chunk_num = g_valid_initrd_count;
1371 ventoy_linux_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
1372
1373 /* part 5: virt chunk */
1374 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
1375 chain->virt_chunk_num = g_valid_initrd_count;
1376 ventoy_linux_fill_virt_data(isosize, chain);
1377
1378 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1379 }
1380