]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_linux.c
ad871a10e5b7a15c1bbde8ec7184675a9737e3bd
[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 void ventoy_cpio_newc_fill_int(grub_uint32_t value, char *buf, int buflen)
568 {
569 int i;
570 int len;
571 char intbuf[32];
572
573 len = grub_snprintf(intbuf, sizeof(intbuf), "%x", value);
574
575 for (i = 0; i < buflen; i++)
576 {
577 buf[i] = '0';
578 }
579
580 if (len > buflen)
581 {
582 grub_printf("int buf len overflow %d %d\n", len, buflen);
583 }
584 else
585 {
586 grub_memcpy(buf + buflen - len, intbuf, len);
587 }
588 }
589
590 int ventoy_cpio_newc_fill_head(void *buf, int filesize, const void *filedata, const char *name)
591 {
592 int namelen = 0;
593 int headlen = 0;
594 static grub_uint32_t cpio_ino = 0xFFFFFFF0;
595 cpio_newc_header *cpio = (cpio_newc_header *)buf;
596
597 namelen = grub_strlen(name) + 1;
598 headlen = sizeof(cpio_newc_header) + namelen;
599 headlen = ventoy_align(headlen, 4);
600
601 grub_memset(cpio, '0', sizeof(cpio_newc_header));
602 grub_memset(cpio + 1, 0, headlen - sizeof(cpio_newc_header));
603
604 grub_memcpy(cpio->c_magic, "070701", 6);
605 ventoy_cpio_newc_fill_int(cpio_ino--, cpio->c_ino, 8);
606 ventoy_cpio_newc_fill_int(0100777, cpio->c_mode, 8);
607 ventoy_cpio_newc_fill_int(1, cpio->c_nlink, 8);
608 ventoy_cpio_newc_fill_int(filesize, cpio->c_filesize, 8);
609 ventoy_cpio_newc_fill_int(namelen, cpio->c_namesize, 8);
610 grub_memcpy(cpio + 1, name, namelen);
611
612 if (filedata)
613 {
614 grub_memcpy((char *)cpio + headlen, filedata, filesize);
615 }
616
617 return headlen;
618 }
619
620 static grub_uint32_t ventoy_linux_get_virt_chunk_size(void)
621 {
622 return (sizeof(ventoy_virt_chunk) + g_ventoy_cpio_size) * g_valid_initrd_count;
623 }
624
625 static void ventoy_linux_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
626 {
627 int id = 0;
628 initrd_info *node;
629 grub_uint64_t sector;
630 grub_uint32_t offset;
631 grub_uint32_t cpio_secs;
632 grub_uint32_t initrd_secs;
633 char *override;
634 ventoy_virt_chunk *cur;
635 char name[32];
636
637 override = (char *)chain + chain->virt_chunk_offset;
638 sector = (isosize + 2047) / 2048;
639 cpio_secs = g_ventoy_cpio_size / 2048;
640
641 offset = g_valid_initrd_count * sizeof(ventoy_virt_chunk);
642 cur = (ventoy_virt_chunk *)override;
643
644 for (node = g_initrd_img_list; node; node = node->next)
645 {
646 if (node->size == 0)
647 {
648 continue;
649 }
650
651 initrd_secs = (grub_uint32_t)((node->size + 2047) / 2048);
652
653 cur->mem_sector_start = sector;
654 cur->mem_sector_end = cur->mem_sector_start + cpio_secs;
655 cur->mem_sector_offset = offset;
656 cur->remap_sector_start = cur->mem_sector_end;
657 cur->remap_sector_end = cur->remap_sector_start + initrd_secs;
658 cur->org_sector_start = (grub_uint32_t)(node->offset / 2048);
659
660 grub_memcpy(g_ventoy_runtime_buf, &chain->os_param, sizeof(ventoy_os_param));
661
662 grub_memset(name, 0, 16);
663 grub_snprintf(name, sizeof(name), "initrd%03d", ++id);
664
665 grub_memcpy(g_ventoy_initrd_head + 1, name, 16);
666 ventoy_cpio_newc_fill_int((grub_uint32_t)node->size, g_ventoy_initrd_head->c_filesize, 8);
667
668 grub_memcpy(override + offset, g_ventoy_cpio_buf, g_ventoy_cpio_size);
669
670 chain->virt_img_size_in_bytes += g_ventoy_cpio_size + initrd_secs * 2048;
671
672 offset += g_ventoy_cpio_size;
673 sector += cpio_secs + initrd_secs;
674 cur++;
675 }
676
677 return;
678 }
679
680 static grub_uint32_t ventoy_linux_get_override_chunk_size(void)
681 {
682 return sizeof(ventoy_override_chunk) * g_valid_initrd_count;
683 }
684
685 static void ventoy_linux_fill_override_data( grub_uint64_t isosize, void *override)
686 {
687 initrd_info *node;
688 grub_uint32_t mod;
689 grub_uint32_t newlen;
690 grub_uint64_t sector;
691 ventoy_override_chunk *cur;
692
693 sector = (isosize + 2047) / 2048;
694
695 cur = (ventoy_override_chunk *)override;
696 for (node = g_initrd_img_list; node; node = node->next)
697 {
698 if (node->size == 0)
699 {
700 continue;
701 }
702
703 newlen = (grub_uint32_t)(node->size + g_ventoy_cpio_size);
704 mod = newlen % 4;
705 if (mod > 0)
706 {
707 newlen += 4 - mod;
708 }
709
710 if (node->iso_type == 0)
711 {
712 ventoy_iso9660_override *dirent = (ventoy_iso9660_override *)node->override_data;
713
714 node->override_length = sizeof(ventoy_iso9660_override);
715 dirent->first_sector = (grub_uint32_t)sector;
716 dirent->size = newlen;
717 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
718 dirent->size_be = grub_swap_bytes32(dirent->size);
719
720 sector += (dirent->size + 2047) / 2048;
721 }
722 else
723 {
724 ventoy_udf_override *udf = (ventoy_udf_override *)node->override_data;
725
726 node->override_length = sizeof(ventoy_udf_override);
727 udf->length = newlen;
728 udf->position = (grub_uint32_t)sector - node->udf_start_block;
729
730 sector += (udf->length + 2047) / 2048;
731 }
732
733 cur->img_offset = node->override_offset;
734 cur->override_size = node->override_length;
735 grub_memcpy(cur->override_data, node->override_data, cur->override_size);
736 cur++;
737 }
738
739 return;
740 }
741
742 grub_err_t ventoy_cmd_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args)
743 {
744 char buf[32] = {0};
745
746 (void)ctxt;
747 (void)argc;
748 (void)args;
749
750 if (argc == 1)
751 {
752 grub_snprintf(buf, sizeof(buf), "%d", g_initrd_img_count);
753 grub_env_set(args[0], buf);
754 }
755
756 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
757 }
758
759 grub_err_t ventoy_cmd_valid_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args)
760 {
761 char buf[32] = {0};
762
763 (void)ctxt;
764 (void)argc;
765 (void)args;
766
767 if (argc == 1)
768 {
769 grub_snprintf(buf, sizeof(buf), "%d", g_valid_initrd_count);
770 grub_env_set(args[0], buf);
771 }
772
773 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
774 }
775
776 static grub_err_t ventoy_linux_locate_initrd(int filt, int *filtcnt)
777 {
778 int data;
779 int filtbysize = 1;
780 int sizefilt = 0;
781 grub_file_t file;
782 initrd_info *node;
783
784 debug("ventoy_linux_locate_initrd %d\n", filt);
785
786 g_valid_initrd_count = 0;
787
788 if (grub_env_get("INITRD_NO_SIZE_FILT"))
789 {
790 filtbysize = 0;
791 }
792
793 for (node = g_initrd_img_list; node; node = node->next)
794 {
795 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(loop)%s", node->name);
796 if (!file)
797 {
798 continue;
799 }
800
801 debug("file <%s> size:%d\n", node->name, (int)file->size);
802
803 /* initrd file too small */
804 if (filtbysize
805 && (NULL == grub_strstr(node->name, "minirt.gz"))
806 && (NULL == grub_strstr(node->name, "initrd.xz"))
807 )
808 {
809 if (filt > 0 && file->size <= g_ventoy_cpio_size + 2048)
810 {
811 debug("file size too small %d\n", (int)g_ventoy_cpio_size);
812 grub_file_close(file);
813 sizefilt++;
814 continue;
815 }
816 }
817
818 if (grub_strcmp(file->fs->name, "iso9660") == 0)
819 {
820 node->iso_type = 0;
821 node->override_offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
822
823 grub_file_read(file, &data, 1); // just read for hook trigger
824 node->offset = grub_iso9660_get_last_read_pos(file);
825 }
826 else
827 {
828 /* TBD */
829 }
830
831 node->size = file->size;
832 g_valid_initrd_count++;
833
834 grub_file_close(file);
835 }
836
837 *filtcnt = sizefilt;
838
839 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
840 }
841
842
843 grub_err_t ventoy_cmd_linux_get_main_initrd_index(grub_extcmd_context_t ctxt, int argc, char **args)
844 {
845 int index = 0;
846 char buf[32];
847 initrd_info *node = NULL;
848
849 (void)ctxt;
850 (void)argc;
851 (void)args;
852
853 if (argc != 1)
854 {
855 return 1;
856 }
857
858 if (g_initrd_img_count == 1)
859 {
860 ventoy_set_env(args[0], "0");
861 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
862 }
863
864 for (node = g_initrd_img_list; node; node = node->next)
865 {
866 if (node->size <= 0)
867 {
868 continue;
869 }
870
871 if (grub_strstr(node->name, "ucode") || grub_strstr(node->name, "-firmware"))
872 {
873 index++;
874 continue;
875 }
876
877 grub_snprintf(buf, sizeof(buf), "%d", index);
878 ventoy_set_env(args[0], buf);
879 break;
880 }
881
882 debug("main initrd index:%d\n", index);
883
884 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
885 }
886
887 grub_err_t ventoy_cmd_linux_locate_initrd(grub_extcmd_context_t ctxt, int argc, char **args)
888 {
889 int sizefilt = 0;
890
891 (void)ctxt;
892 (void)argc;
893 (void)args;
894
895 ventoy_linux_locate_initrd(1, &sizefilt);
896
897 if (g_valid_initrd_count == 0 && sizefilt > 0)
898 {
899 ventoy_linux_locate_initrd(0, &sizefilt);
900 }
901
902 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
903 }
904
905 grub_err_t ventoy_cmd_load_cpio(grub_extcmd_context_t ctxt, int argc, char **args)
906 {
907 int rc;
908 char *template_file = NULL;
909 char *template_buf = NULL;
910 char *persistent_buf = NULL;
911 char *injection_buf = NULL;
912 const char *injection_file = NULL;
913 grub_uint8_t *buf = NULL;
914 grub_uint32_t mod;
915 grub_uint32_t headlen;
916 grub_uint32_t initrd_head_len;
917 grub_uint32_t padlen;
918 grub_uint32_t img_chunk_size;
919 grub_uint32_t template_size = 0;
920 grub_uint32_t persistent_size = 0;
921 grub_uint32_t injection_size = 0;
922 grub_file_t file;
923 grub_file_t tmpfile;
924 ventoy_img_chunk_list chunk_list;
925
926 (void)ctxt;
927 (void)argc;
928
929 if (argc != 3)
930 {
931 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s cpiofile\n", cmd_raw_name);
932 }
933
934 if (g_img_chunk_list.chunk == NULL || g_img_chunk_list.cur_chunk == 0)
935 {
936 return grub_error(GRUB_ERR_BAD_ARGUMENT, "image chunk is null\n");
937 }
938
939 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
940
941 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
942 if (!file)
943 {
944 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]);
945 }
946
947 if (g_ventoy_cpio_buf)
948 {
949 grub_free(g_ventoy_cpio_buf);
950 g_ventoy_cpio_buf = NULL;
951 g_ventoy_cpio_size = 0;
952 }
953
954 rc = ventoy_plugin_get_persistent_chunklist(args[1], -1, &chunk_list);
955 if (rc == 0 && chunk_list.cur_chunk > 0 && chunk_list.chunk)
956 {
957 persistent_size = chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
958 persistent_buf = (char *)(chunk_list.chunk);
959 }
960
961 template_file = ventoy_plugin_get_cur_install_template(args[1]);
962 if (template_file)
963 {
964 debug("auto install template: <%s>\n", template_file);
965 tmpfile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", args[2], template_file);
966 if (tmpfile)
967 {
968 debug("auto install script size %d\n", (int)tmpfile->size);
969 template_size = tmpfile->size;
970 template_buf = grub_malloc(template_size);
971 if (template_buf)
972 {
973 grub_file_read(tmpfile, template_buf, template_size);
974 }
975
976 grub_file_close(tmpfile);
977 }
978 else
979 {
980 debug("Failed to open install script %s%s\n", args[2], template_file);
981 }
982 }
983 else
984 {
985 debug("auto install script skipped or not configed %s\n", args[1]);
986 }
987
988 injection_file = ventoy_plugin_get_injection(args[1]);
989 if (injection_file)
990 {
991 debug("injection archive: <%s>\n", injection_file);
992 tmpfile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", args[2], injection_file);
993 if (tmpfile)
994 {
995 debug("injection archive size:%d\n", (int)tmpfile->size);
996 injection_size = tmpfile->size;
997 injection_buf = grub_malloc(injection_size);
998 if (injection_buf)
999 {
1000 grub_file_read(tmpfile, injection_buf, injection_size);
1001 }
1002
1003 grub_file_close(tmpfile);
1004 }
1005 else
1006 {
1007 debug("Failed to open injection archive %s%s\n", args[2], injection_file);
1008 }
1009 }
1010 else
1011 {
1012 debug("injection not configed %s\n", args[1]);
1013 }
1014
1015 g_ventoy_cpio_buf = grub_malloc(file->size + 4096 + template_size + persistent_size + injection_size + img_chunk_size);
1016 if (NULL == g_ventoy_cpio_buf)
1017 {
1018 grub_file_close(file);
1019 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't alloc memory %llu\n", file->size + 4096 + img_chunk_size);
1020 }
1021
1022 grub_file_read(file, g_ventoy_cpio_buf, file->size);
1023
1024 buf = (grub_uint8_t *)(g_ventoy_cpio_buf + file->size - 4);
1025 while (*((grub_uint32_t *)buf) != 0x37303730)
1026 {
1027 buf -= 4;
1028 }
1029
1030 /* get initrd head len */
1031 initrd_head_len = ventoy_cpio_newc_fill_head(buf, 0, NULL, "initrd000.xx");
1032
1033 /* step1: insert image chunk data to cpio */
1034 headlen = ventoy_cpio_newc_fill_head(buf, img_chunk_size, g_img_chunk_list.chunk, "ventoy/ventoy_image_map");
1035 buf += headlen + ventoy_align(img_chunk_size, 4);
1036
1037 if (template_buf)
1038 {
1039 headlen = ventoy_cpio_newc_fill_head(buf, template_size, template_buf, "ventoy/autoinstall");
1040 buf += headlen + ventoy_align(template_size, 4);
1041 }
1042
1043 if (persistent_size > 0 && persistent_buf)
1044 {
1045 headlen = ventoy_cpio_newc_fill_head(buf, persistent_size, persistent_buf, "ventoy/ventoy_persistent_map");
1046 buf += headlen + ventoy_align(persistent_size, 4);
1047
1048 grub_free(persistent_buf);
1049 persistent_buf = NULL;
1050 }
1051
1052 if (injection_size > 0 && injection_buf)
1053 {
1054 headlen = ventoy_cpio_newc_fill_head(buf, injection_size, injection_buf, "ventoy/ventoy_injection");
1055 buf += headlen + ventoy_align(injection_size, 4);
1056
1057 grub_free(injection_buf);
1058 injection_buf = NULL;
1059 }
1060
1061 /* step2: insert os param to cpio */
1062 headlen = ventoy_cpio_newc_fill_head(buf, 0, NULL, "ventoy/ventoy_os_param");
1063 padlen = sizeof(ventoy_os_param);
1064 g_ventoy_cpio_size = (grub_uint32_t)(buf - g_ventoy_cpio_buf) + headlen + padlen + initrd_head_len;
1065 mod = g_ventoy_cpio_size % 2048;
1066 if (mod)
1067 {
1068 g_ventoy_cpio_size += 2048 - mod;
1069 padlen += 2048 - mod;
1070 }
1071
1072 /* update os param data size, the data will be updated before chain boot */
1073 ventoy_cpio_newc_fill_int(padlen, ((cpio_newc_header *)buf)->c_filesize, 8);
1074 g_ventoy_runtime_buf = (grub_uint8_t *)buf + headlen;
1075
1076 /* step3: fill initrd cpio head, the file size will be updated before chain boot */
1077 g_ventoy_initrd_head = (cpio_newc_header *)(g_ventoy_runtime_buf + padlen);
1078 ventoy_cpio_newc_fill_head(g_ventoy_initrd_head, 0, NULL, "initrd000.xx");
1079
1080 grub_file_close(file);
1081
1082 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1083 }
1084
1085
1086 grub_err_t ventoy_cmd_linux_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
1087 {
1088 int ventoy_compatible = 0;
1089 grub_uint32_t size = 0;
1090 grub_uint64_t isosize = 0;
1091 grub_uint32_t boot_catlog = 0;
1092 grub_uint32_t img_chunk_size = 0;
1093 grub_uint32_t override_size = 0;
1094 grub_uint32_t virt_chunk_size = 0;
1095 grub_file_t file;
1096 grub_disk_t disk;
1097 const char *pLastChain = NULL;
1098 const char *compatible;
1099 ventoy_chain_head *chain;
1100 char envbuf[64];
1101
1102 (void)ctxt;
1103 (void)argc;
1104
1105 compatible = grub_env_get("ventoy_compatible");
1106 if (compatible && compatible[0] == 'Y')
1107 {
1108 ventoy_compatible = 1;
1109 }
1110
1111 if ((NULL == g_img_chunk_list.chunk) || (0 == ventoy_compatible && g_ventoy_cpio_buf == NULL))
1112 {
1113 grub_printf("ventoy not ready\n");
1114 return 1;
1115 }
1116
1117 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
1118 if (!file)
1119 {
1120 return 1;
1121 }
1122
1123 isosize = file->size;
1124
1125 boot_catlog = ventoy_get_iso_boot_catlog(file);
1126 if (boot_catlog)
1127 {
1128 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
1129 {
1130 grub_env_set("LoadIsoEfiDriver", "on");
1131 }
1132 }
1133 else
1134 {
1135 if (ventoy_is_efi_os())
1136 {
1137 grub_env_set("LoadIsoEfiDriver", "on");
1138 }
1139 else
1140 {
1141 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
1142 }
1143 }
1144
1145 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1146
1147 if (ventoy_compatible)
1148 {
1149 size = sizeof(ventoy_chain_head) + img_chunk_size;
1150 }
1151 else
1152 {
1153 override_size = ventoy_linux_get_override_chunk_size();
1154 virt_chunk_size = ventoy_linux_get_virt_chunk_size();
1155 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
1156 }
1157
1158 pLastChain = grub_env_get("vtoy_chain_mem_addr");
1159 if (pLastChain)
1160 {
1161 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
1162 if (chain)
1163 {
1164 debug("free last chain memory %p\n", chain);
1165 grub_free(chain);
1166 }
1167 }
1168
1169 chain = grub_malloc(size);
1170 if (!chain)
1171 {
1172 grub_printf("Failed to alloc chain memory size %u\n", size);
1173 grub_file_close(file);
1174 return 1;
1175 }
1176
1177 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
1178 grub_env_set("vtoy_chain_mem_addr", envbuf);
1179 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
1180 grub_env_set("vtoy_chain_mem_size", envbuf);
1181
1182 grub_memset(chain, 0, sizeof(ventoy_chain_head));
1183
1184 /* part 1: os parameter */
1185 g_ventoy_chain_type = ventoy_chain_linux;
1186 ventoy_fill_os_param(file, &(chain->os_param));
1187
1188 /* part 2: chain head */
1189 disk = file->device->disk;
1190 chain->disk_drive = disk->id;
1191 chain->disk_sector_size = (1 << disk->log_sector_size);
1192 chain->real_img_size_in_bytes = file->size;
1193 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
1194 chain->boot_catalog = boot_catlog;
1195
1196 if (!ventoy_is_efi_os())
1197 {
1198 grub_file_seek(file, boot_catlog * 2048);
1199 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
1200 }
1201
1202 /* part 3: image chunk */
1203 chain->img_chunk_offset = sizeof(ventoy_chain_head);
1204 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
1205 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
1206
1207 if (ventoy_compatible)
1208 {
1209 return 0;
1210 }
1211
1212 if (g_valid_initrd_count == 0)
1213 {
1214 return 0;
1215 }
1216
1217 /* part 4: override chunk */
1218 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
1219 chain->override_chunk_num = g_valid_initrd_count;
1220 ventoy_linux_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
1221
1222 /* part 5: virt chunk */
1223 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
1224 chain->virt_chunk_num = g_valid_initrd_count;
1225 ventoy_linux_fill_virt_data(isosize, chain);
1226
1227 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1228 }
1229