1 /******************************************************************************
4 * Copyright (c) 2020, longpanda <admin@ventoy.net>
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.
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.
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/>.
21 #include <grub/types.h>
22 #include <grub/misc.h>
26 #include <grub/disk.h>
27 #include <grub/device.h>
28 #include <grub/term.h>
29 #include <grub/partition.h>
30 #include <grub/file.h>
31 #include <grub/normal.h>
32 #include <grub/extcmd.h>
33 #include <grub/datetime.h>
34 #include <grub/i18n.h>
36 #ifdef GRUB_MACHINE_EFI
37 #include <grub/efi/efi.h>
39 #include <grub/time.h>
40 #include <grub/ventoy.h>
41 #include "ventoy_def.h"
43 GRUB_MOD_LICENSE ("GPLv3+");
45 int g_ventoy_debug
= 0;
46 static int g_efi_os
= 0xFF;
47 initrd_info
*g_initrd_img_list
= NULL
;
48 initrd_info
*g_initrd_img_tail
= NULL
;
49 int g_initrd_img_count
= 0;
50 int g_valid_initrd_count
= 0;
52 static grub_file_t g_old_file
;
54 char g_img_swap_tmp_buf
[1024];
55 img_info g_img_swap_tmp
;
56 img_info
*g_ventoy_img_list
= NULL
;
57 int g_ventoy_img_count
= 0;
59 grub_device_t g_enum_dev
= NULL
;
60 grub_fs_t g_enum_fs
= NULL
;
61 img_iterator_node g_img_iterator_head
;
62 img_iterator_node
*g_img_iterator_tail
= NULL
;
64 grub_uint8_t g_ventoy_break_level
= 0;
65 grub_uint8_t g_ventoy_debug_level
= 0;
66 grub_uint8_t
*g_ventoy_cpio_buf
= NULL
;
67 grub_uint32_t g_ventoy_cpio_size
= 0;
68 cpio_newc_header
*g_ventoy_initrd_head
= NULL
;
69 grub_uint8_t
*g_ventoy_runtime_buf
= NULL
;
71 ventoy_grub_param
*g_grub_param
= NULL
;
73 ventoy_guid g_ventoy_guid
= VENTOY_GUID
;
75 ventoy_img_chunk_list g_img_chunk_list
;
77 static char *g_tree_script_buf
= NULL
;
78 static int g_tree_script_pos
= 0;
80 static char *g_list_script_buf
= NULL
;
81 static int g_list_script_pos
= 0;
84 void ventoy_debug(const char *fmt
, ...)
89 grub_vprintf (fmt
, args
);
93 int ventoy_is_efi_os(void)
97 g_efi_os
= (grub_strstr(GRUB_PLATFORM
, "efi")) ? 1 : 0;
103 static int ventoy_string_check(const char *str
, grub_char_check_func check
)
122 static grub_ssize_t
ventoy_fs_read(grub_file_t file
, char *buf
, grub_size_t len
)
124 grub_memcpy(buf
, (char *)file
->data
+ file
->offset
, len
);
128 static grub_err_t
ventoy_fs_close(grub_file_t file
)
130 grub_file_close(g_old_file
);
131 grub_free(file
->data
);
139 static grub_file_t
ventoy_wrapper_open(grub_file_t rawFile
, enum grub_file_type type
)
143 static struct grub_fs vtoy_fs
=
148 .fs_read
= ventoy_fs_read
,
149 .fs_close
= ventoy_fs_close
,
159 file
= (grub_file_t
)grub_zalloc(sizeof (*file
));
165 file
->data
= grub_malloc(rawFile
->size
+ 4096);
171 grub_file_read(rawFile
, file
->data
, rawFile
->size
);
172 len
= ventoy_fill_data(4096, (char *)file
->data
+ rawFile
->size
);
174 g_old_file
= rawFile
;
176 file
->size
= rawFile
->size
+ len
;
177 file
->device
= rawFile
->device
;
179 file
->not_easily_seekable
= 1;
184 static int ventoy_check_decimal_var(const char *name
, long *value
)
186 const char *value_str
= NULL
;
188 value_str
= grub_env_get(name
);
189 if (NULL
== value_str
)
191 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Variable %s not found", name
);
194 if (!ventoy_is_decimal(value_str
))
196 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Variable %s value '%s' is not an integer", name
, value_str
);
199 *value
= grub_strtol(value_str
, NULL
, 10);
201 return GRUB_ERR_NONE
;
204 static grub_err_t
ventoy_cmd_debug(grub_extcmd_context_t ctxt
, int argc
, char **args
)
208 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Usage: %s {on|off}", cmd_raw_name
);
211 if (0 == grub_strcmp(args
[0], "on"))
214 grub_env_set("vtdebug_flag", "debug");
219 grub_env_set("vtdebug_flag", "");
222 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
225 static grub_err_t
ventoy_cmd_break(grub_extcmd_context_t ctxt
, int argc
, char **args
)
229 if (argc
< 1 || (args
[0][0] != '0' && args
[0][0] != '1'))
231 grub_printf("Usage: %s {level} [debug]\r\n", cmd_raw_name
);
232 grub_printf(" level:\r\n");
233 grub_printf(" 01/11: busybox / (+cat log)\r\n");
234 grub_printf(" 02/12: initrd / (+cat log)\r\n");
235 grub_printf(" 03/13: hook / (+cat log)\r\n");
237 grub_printf(" debug:\r\n");
238 grub_printf(" 0: debug is on\r\n");
239 grub_printf(" 1: debug is off\r\n");
241 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
244 g_ventoy_break_level
= (grub_uint8_t
)grub_strtoul(args
[0], NULL
, 16);
246 if (argc
> 1 && grub_strtoul(args
[1], NULL
, 10) > 0)
248 g_ventoy_debug_level
= 1;
251 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
254 static grub_err_t
ventoy_cmd_incr(grub_extcmd_context_t ctxt
, int argc
, char **args
)
259 if ((argc
!= 2) || (!ventoy_is_decimal(args
[1])))
261 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Usage: %s {Variable} {Int}", cmd_raw_name
);
264 if (GRUB_ERR_NONE
!= ventoy_check_decimal_var(args
[0], &value_long
))
269 value_long
+= grub_strtol(args
[1], NULL
, 10);
271 grub_snprintf(buf
, sizeof(buf
), "%ld", value_long
);
272 grub_env_set(args
[0], buf
);
274 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
277 static grub_err_t
ventoy_cmd_file_size(grub_extcmd_context_t ctxt
, int argc
, char **args
)
292 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
295 debug("failed to open file <%s> for udf check\n", args
[0]);
299 grub_snprintf(buf
, sizeof(buf
), "%llu", (unsigned long long)file
->size
);
301 grub_env_set(args
[1], buf
);
303 grub_file_close(file
);
309 static grub_err_t
ventoy_cmd_load_iso_to_mem(grub_extcmd_context_t ctxt
, int argc
, char **args
)
326 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
329 debug("failed to open file <%s> for udf check\n", args
[0]);
333 #ifdef GRUB_MACHINE_EFI
334 buf
= (char *)grub_efi_allocate_iso_buf(file
->size
);
336 buf
= (char *)grub_malloc(file
->size
);
339 grub_file_read(file
, buf
, file
->size
);
341 grub_snprintf(name
, sizeof(name
), "%s_addr", args
[1]);
342 grub_snprintf(value
, sizeof(value
), "0x%llx", (unsigned long long)(unsigned long)buf
);
343 grub_env_set(name
, value
);
345 grub_snprintf(name
, sizeof(name
), "%s_size", args
[1]);
346 grub_snprintf(value
, sizeof(value
), "%llu", (unsigned long long)file
->size
);
347 grub_env_set(name
, value
);
349 grub_file_close(file
);
355 static grub_err_t
ventoy_cmd_is_udf(grub_extcmd_context_t ctxt
, int argc
, char **args
)
360 grub_uint8_t buf
[32];
371 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
374 debug("failed to open file <%s> for udf check\n", args
[0]);
378 for (i
= 16; i
< 32; i
++)
380 grub_file_seek(file
, i
* 2048);
381 grub_file_read(file
, buf
, sizeof(buf
));
389 grub_file_seek(file
, i
* 2048);
390 grub_file_read(file
, buf
, sizeof(buf
));
392 if (grub_memcmp(buf
+ 1, "BEA01", 5) == 0)
395 grub_file_seek(file
, i
* 2048);
396 grub_file_read(file
, buf
, sizeof(buf
));
398 if (grub_memcmp(buf
+ 1, "NSR02", 5) == 0 ||
399 grub_memcmp(buf
+ 1, "NSR03", 5) == 0)
405 grub_file_close(file
);
407 debug("ISO UDF: %s\n", rc
? "NO" : "YES");
412 static grub_err_t
ventoy_cmd_cmp(grub_extcmd_context_t ctxt
, int argc
, char **args
)
414 long value_long1
= 0;
415 long value_long2
= 0;
417 if ((argc
!= 3) || (!ventoy_is_decimal(args
[0])) || (!ventoy_is_decimal(args
[2])))
419 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Usage: %s {Int1} { eq|ne|gt|lt|ge|le } {Int2}", cmd_raw_name
);
422 value_long1
= grub_strtol(args
[0], NULL
, 10);
423 value_long2
= grub_strtol(args
[2], NULL
, 10);
425 if (0 == grub_strcmp(args
[1], "eq"))
427 grub_errno
= (value_long1
== value_long2
) ? GRUB_ERR_NONE
: GRUB_ERR_TEST_FAILURE
;
429 else if (0 == grub_strcmp(args
[1], "ne"))
431 grub_errno
= (value_long1
!= value_long2
) ? GRUB_ERR_NONE
: GRUB_ERR_TEST_FAILURE
;
433 else if (0 == grub_strcmp(args
[1], "gt"))
435 grub_errno
= (value_long1
> value_long2
) ? GRUB_ERR_NONE
: GRUB_ERR_TEST_FAILURE
;
437 else if (0 == grub_strcmp(args
[1], "lt"))
439 grub_errno
= (value_long1
< value_long2
) ? GRUB_ERR_NONE
: GRUB_ERR_TEST_FAILURE
;
441 else if (0 == grub_strcmp(args
[1], "ge"))
443 grub_errno
= (value_long1
>= value_long2
) ? GRUB_ERR_NONE
: GRUB_ERR_TEST_FAILURE
;
445 else if (0 == grub_strcmp(args
[1], "le"))
447 grub_errno
= (value_long1
<= value_long2
) ? GRUB_ERR_NONE
: GRUB_ERR_TEST_FAILURE
;
451 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Usage: %s {Int1} { eq ne gt lt ge le } {Int2}", cmd_raw_name
);
457 static grub_err_t
ventoy_cmd_device(grub_extcmd_context_t ctxt
, int argc
, char **args
)
464 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Usage: %s path var", cmd_raw_name
);
467 grub_strncpy(buf
, (args
[0][0] == '(') ? args
[0] + 1 : args
[0], sizeof(buf
) - 1);
468 pos
= grub_strstr(buf
, ",");
474 grub_env_set(args
[1], buf
);
476 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
479 static grub_err_t
ventoy_cmd_check_compatible(grub_extcmd_context_t ctxt
, int argc
, char **args
)
485 const char *files
[] = { "ventoy.dat", "VENTOY.DAT" };
491 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Usage: %s (loop)", cmd_raw_name
);
494 for (i
= 0; i
< (int)ARRAY_SIZE(files
); i
++)
496 grub_snprintf(buf
, sizeof(buf
) - 1, "[ -e %s/%s ]", args
[0], files
[i
]);
497 if (0 == grub_script_execute_sourcecode(buf
))
499 debug("file %s exist, ventoy_compatible YES\n", buf
);
500 grub_env_set("ventoy_compatible", "YES");
501 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
505 debug("file %s NOT exist\n", buf
);
509 grub_snprintf(buf
, sizeof(buf
) - 1, "%s", args
[0][0] == '(' ? (args
[0] + 1) : args
[0]);
510 pos
= grub_strstr(buf
, ")");
516 disk
= grub_disk_open(buf
);
519 grub_disk_read(disk
, 16 << 2, 0, 1024, g_img_swap_tmp_buf
);
520 grub_disk_close(disk
);
522 g_img_swap_tmp_buf
[703] = 0;
523 for (i
= 319; i
< 703; i
++)
525 if (g_img_swap_tmp_buf
[i
] == 'V' &&
526 0 == grub_strncmp(g_img_swap_tmp_buf
+ i
, VENTOY_COMPATIBLE_STR
, VENTOY_COMPATIBLE_STR_LEN
))
528 debug("Ventoy compatible string exist at %d, ventoy_compatible YES\n", i
);
529 grub_env_set("ventoy_compatible", "YES");
530 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
536 debug("failed to open disk <%s>\n", buf
);
539 grub_env_set("ventoy_compatible", "NO");
540 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
543 int ventoy_cmp_img(img_info
*img1
, img_info
*img2
)
549 for (s1
= img1
->name
, s2
= img2
->name
; *s1
&& *s2
; s1
++, s2
++)
554 if (grub_islower(c1
))
559 if (grub_islower(c2
))
573 void ventoy_swap_img(img_info
*img1
, img_info
*img2
)
575 grub_memcpy(&g_img_swap_tmp
, img1
, sizeof(img_info
));
577 grub_memcpy(img1
, img2
, sizeof(img_info
));
578 img1
->next
= g_img_swap_tmp
.next
;
579 img1
->prev
= g_img_swap_tmp
.prev
;
581 g_img_swap_tmp
.next
= img2
->next
;
582 g_img_swap_tmp
.prev
= img2
->prev
;
583 grub_memcpy(img2
, &g_img_swap_tmp
, sizeof(img_info
));
586 static int ventoy_img_name_valid(const char *filename
, grub_size_t namelen
)
590 for (i
= 0; i
< namelen
; i
++)
592 if (filename
[i
] == ' ' || filename
[i
] == '\t')
597 if ((grub_uint8_t
)(filename
[i
]) >= 127)
606 static int ventoy_check_ignore_flag(const char *filename
, const struct grub_dirhook_info
*info
, void *data
)
610 if (filename
&& filename
[0] == '.' && 0 == grub_strncmp(filename
, ".ventoyignore", 13))
620 static int ventoy_colect_img_files(const char *filename
, const struct grub_dirhook_info
*info
, void *data
)
626 img_iterator_node
*tmp
;
627 img_iterator_node
*new_node
;
628 img_iterator_node
*node
= (img_iterator_node
*)data
;
630 len
= grub_strlen(filename
);
634 if ((len
== 1 && filename
[0] == '.') ||
635 (len
== 2 && filename
[0] == '.' && filename
[1] == '.'))
640 if (!ventoy_img_name_valid(filename
, len
))
645 if (filename
[0] == '$' && 0 == grub_strncmp(filename
, "$RECYCLE.BIN", 12))
650 new_node
= grub_zalloc(sizeof(img_iterator_node
));
653 new_node
->dirlen
= grub_snprintf(new_node
->dir
, sizeof(new_node
->dir
), "%s%s/", node
->dir
, filename
);
655 g_enum_fs
->fs_dir(g_enum_dev
, new_node
->dir
, ventoy_check_ignore_flag
, &ignore
);
658 debug("Directory %s ignored...\n", new_node
->dir
);
663 new_node
->tail
= node
->tail
;
665 new_node
->parent
= node
;
666 if (!node
->firstchild
)
668 node
->firstchild
= new_node
;
671 if (g_img_iterator_tail
)
673 g_img_iterator_tail
->next
= new_node
;
674 g_img_iterator_tail
= new_node
;
678 g_img_iterator_head
.next
= new_node
;
679 g_img_iterator_tail
= new_node
;
685 debug("Find a file %s\n", filename
);
687 if ((len
> 4) && (0 == grub_strcasecmp(filename
+ len
- 4, ".iso")))
689 if (!ventoy_img_name_valid(filename
, len
))
694 img
= grub_zalloc(sizeof(img_info
));
697 grub_snprintf(img
->name
, sizeof(img
->name
), "%s", filename
);
698 grub_snprintf(img
->path
, sizeof(img
->path
), "%s%s", node
->dir
, filename
);
700 if (g_ventoy_img_list
)
702 tail
= *(node
->tail
);
708 g_ventoy_img_list
= img
;
711 img
->size
= info
->size
;
712 img
->id
= g_ventoy_img_count
;
714 if (node
&& NULL
== node
->firstiso
)
716 node
->firstiso
= img
;
727 *((img_info
**)(node
->tail
)) = img
;
728 g_ventoy_img_count
++;
730 debug("Add %s%s to list %d\n", node
->dir
, filename
, g_ventoy_img_count
);
738 int ventoy_fill_data(grub_uint32_t buflen
, char *buffer
)
740 int len
= GRUB_UINT_MAX
;
741 const char *value
= NULL
;
744 char guidstr
[32] = {0};
745 ventoy_guid guid
= VENTOY_GUID
;
746 const char *fmt1
= NULL
;
747 const char *fmt2
= NULL
;
748 const char *fmt3
= NULL
;
749 grub_uint32_t
*puint
= (grub_uint32_t
*)name
;
750 grub_uint32_t
*puint2
= (grub_uint32_t
*)plat
;
751 const char fmtdata
[]={ 0x39, 0x35, 0x25, 0x00, 0x35, 0x00, 0x23, 0x30, 0x30, 0x30, 0x30, 0x66, 0x66, 0x00 };
752 const char fmtcode
[]={
753 0x22, 0x0A, 0x2B, 0x20, 0x68, 0x62, 0x6F, 0x78, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x74, 0x6F, 0x70,
754 0x20, 0x3D, 0x20, 0x25, 0x73, 0x0A, 0x20, 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x3D, 0x20, 0x25,
755 0x73, 0x0A, 0x20, 0x20, 0x2B, 0x20, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x20, 0x7B, 0x74, 0x65, 0x78,
756 0x74, 0x20, 0x3D, 0x20, 0x22, 0x25, 0x73, 0x20, 0x25, 0x73, 0x25, 0x73, 0x22, 0x20, 0x63, 0x6F,
757 0x6C, 0x6F, 0x72, 0x20, 0x3D, 0x20, 0x22, 0x25, 0x73, 0x22, 0x20, 0x61, 0x6C, 0x69, 0x67, 0x6E,
758 0x20, 0x3D, 0x20, 0x22, 0x6C, 0x65, 0x66, 0x74, 0x22, 0x7D, 0x0A, 0x7D, 0x0A, 0x22, 0x00
761 grub_memset(name
, 0, sizeof(name
));
762 puint
[0] = grub_swap_bytes32(0x56454e54);
763 puint
[3] = grub_swap_bytes32(0x4f4e0000);
764 puint
[2] = grub_swap_bytes32(0x45525349);
765 puint
[1] = grub_swap_bytes32(0x4f595f56);
766 value
= ventoy_get_env(name
);
768 grub_memset(name
, 0, sizeof(name
));
769 puint
[1] = grub_swap_bytes32(0x5f544f50);
770 puint
[0] = grub_swap_bytes32(0x56544c45);
771 fmt1
= ventoy_get_env(name
);
777 grub_memset(name
, 0, sizeof(name
));
778 puint
[1] = grub_swap_bytes32(0x5f4c4654);
779 puint
[0] = grub_swap_bytes32(0x56544c45);
780 fmt2
= ventoy_get_env(name
);
782 grub_memset(name
, 0, sizeof(name
));
783 puint
[1] = grub_swap_bytes32(0x5f434c52);
784 puint
[0] = grub_swap_bytes32(0x56544c45);
785 fmt3
= ventoy_get_env(name
);
787 grub_memcpy(guidstr
, &guid
, sizeof(guid
));
789 #if defined (GRUB_MACHINE_EFI)
790 puint2
[0] = grub_swap_bytes32(0x55454649);
792 puint2
[0] = grub_swap_bytes32(0x42494f53);
795 /* Easter egg :) It will be appreciated if you reserve it, but NOT mandatory. */
796 #pragma GCC diagnostic push
797 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
798 len
= grub_snprintf(buffer
, buflen
, fmtcode
,
799 fmt1
? fmt1
: fmtdata
,
800 fmt2
? fmt2
: fmtdata
+ 4,
801 value
? value
: "", plat
, guidstr
,
802 fmt3
? fmt3
: fmtdata
+ 6);
803 #pragma GCC diagnostic pop
805 grub_memset(name
, 0, sizeof(name
));
806 puint
[0] = grub_swap_bytes32(0x76746f79);
807 puint
[2] = grub_swap_bytes32(0x656e7365);
808 puint
[1] = grub_swap_bytes32(0x5f6c6963);
809 ventoy_set_env(name
, guidstr
);
814 static img_info
* ventoy_get_min_iso(img_iterator_node
*node
)
816 img_info
*minimg
= NULL
;
817 img_info
*img
= (img_info
*)(node
->firstiso
);
819 while (img
&& (img_iterator_node
*)(img
->parent
) == node
)
821 if (img
->select
== 0 && (NULL
== minimg
|| grub_strcmp(img
->name
, minimg
->name
) < 0))
836 static img_iterator_node
* ventoy_get_min_child(img_iterator_node
*node
)
838 img_iterator_node
*Minchild
= NULL
;
839 img_iterator_node
*child
= node
->firstchild
;
841 while (child
&& child
->parent
== node
)
843 if (child
->select
== 0 && (NULL
== Minchild
|| grub_strcmp(child
->dir
, Minchild
->dir
) < 0))
852 Minchild
->select
= 1;
858 static int ventoy_dynamic_tree_menu(img_iterator_node
*node
)
862 img_iterator_node
*child
= NULL
;
864 if (node
->isocnt
== 0 || node
->done
== 1)
869 if (node
->parent
&& node
->parent
->dirlen
< node
->dirlen
)
871 offset
= node
->parent
->dirlen
;
874 if (node
!= &g_img_iterator_head
)
876 node
->dir
[node
->dirlen
- 1] = 0;
877 g_tree_script_pos
+= grub_snprintf(g_tree_script_buf
+ g_tree_script_pos
, VTOY_MAX_SCRIPT_BUF
- g_tree_script_pos
,
878 "submenu \"%-10s [%s]\" {\n", "DIR", node
->dir
+ offset
);
881 while ((child
= ventoy_get_min_child(node
)) != NULL
)
883 ventoy_dynamic_tree_menu(child
);
886 while ((img
= ventoy_get_min_iso(node
)) != NULL
)
888 g_tree_script_pos
+= grub_snprintf(g_tree_script_buf
+ g_tree_script_pos
, VTOY_MAX_SCRIPT_BUF
- g_tree_script_pos
,
889 "menuentry \"%-10s %s\" --id=\"VID_%d\" {\n"
890 " common_menuentry \n"
892 grub_get_human_size(img
->size
, GRUB_HUMAN_SIZE_SHORT
), img
->name
, img
->id
);
895 if (node
!= &g_img_iterator_head
)
897 g_tree_script_pos
+= grub_snprintf(g_tree_script_buf
+ g_tree_script_pos
, VTOY_MAX_SCRIPT_BUF
- g_tree_script_pos
, "}\n");
904 static grub_err_t
ventoy_cmd_list_img(grub_extcmd_context_t ctxt
, int argc
, char **args
)
907 grub_device_t dev
= NULL
;
908 img_info
*cur
= NULL
;
909 img_info
*tail
= NULL
;
910 char *device_name
= NULL
;
912 img_iterator_node
*node
= NULL
;
913 img_iterator_node
*tmp
= NULL
;
919 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Usage: %s {device} {cntvar}", cmd_raw_name
);
922 if (g_ventoy_img_list
|| g_ventoy_img_count
)
924 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Must clear image before list");
927 device_name
= grub_file_get_device_name(args
[0]);
933 g_enum_dev
= dev
= grub_device_open(device_name
);
939 g_enum_fs
= fs
= grub_fs_probe(dev
);
945 grub_memset(&g_img_iterator_head
, 0, sizeof(g_img_iterator_head
));
947 g_img_iterator_head
.dirlen
= 1;
948 g_img_iterator_head
.tail
= &tail
;
949 grub_strcpy(g_img_iterator_head
.dir
, "/");
951 for (node
= &g_img_iterator_head
; node
; node
= node
->next
)
953 fs
->fs_dir(dev
, node
->dir
, ventoy_colect_img_files
, node
);
956 for (node
= &g_img_iterator_head
; node
; node
= node
->next
)
958 ventoy_dynamic_tree_menu(node
);
962 node
= g_img_iterator_head
.next
;
970 /* sort image list by image name */
971 for (cur
= g_ventoy_img_list
; cur
; cur
= cur
->next
)
973 for (tail
= cur
->next
; tail
; tail
= tail
->next
)
975 if (ventoy_cmp_img(cur
, tail
) > 0)
977 ventoy_swap_img(cur
, tail
);
982 for (cur
= g_ventoy_img_list
; cur
; cur
= cur
->next
)
984 g_list_script_pos
+= grub_snprintf(g_list_script_buf
+ g_list_script_pos
, VTOY_MAX_SCRIPT_BUF
- g_list_script_pos
,
985 "menuentry \"%s\" --id=\"VID_%d\" {\n"
986 " common_menuentry \n"
990 g_list_script_buf
[g_list_script_pos
] = 0;
992 grub_snprintf(buf
, sizeof(buf
), "%d", g_ventoy_img_count
);
993 grub_env_set(args
[1], buf
);
997 check_free(device_name
, grub_free
);
998 check_free(dev
, grub_device_close
);
1000 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
1004 static grub_err_t
ventoy_cmd_clear_img(grub_extcmd_context_t ctxt
, int argc
, char **args
)
1006 img_info
*next
= NULL
;
1007 img_info
*cur
= g_ventoy_img_list
;
1020 g_ventoy_img_list
= NULL
;
1021 g_ventoy_img_count
= 0;
1023 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
1026 static grub_err_t
ventoy_cmd_img_name(grub_extcmd_context_t ctxt
, int argc
, char **args
)
1029 img_info
*cur
= g_ventoy_img_list
;
1033 if (argc
!= 2 || (!ventoy_is_decimal(args
[0])))
1035 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Usage: %s {imageID} {var}", cmd_raw_name
);
1038 img_id
= grub_strtol(args
[0], NULL
, 10);
1039 if (img_id
>= g_ventoy_img_count
)
1041 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "No such many images %ld %ld", img_id
, g_ventoy_img_count
);
1044 debug("Find image %ld name \n", img_id
);
1046 while (cur
&& img_id
> 0)
1054 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "No such many images");
1057 debug("image name is %s\n", cur
->name
);
1059 grub_env_set(args
[1], cur
->name
);
1061 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
1064 static grub_err_t
ventoy_cmd_chosen_img_path(grub_extcmd_context_t ctxt
, int argc
, char **args
)
1068 const char *id
= NULL
;
1069 img_info
*cur
= g_ventoy_img_list
;
1075 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Usage: %s {var}", cmd_raw_name
);
1078 id
= grub_env_get("chosen");
1080 pos
= grub_strstr(id
, "VID_");
1083 img_id
= (int)grub_strtoul(pos
+ 4, NULL
, 10);
1087 img_id
= (int)grub_strtoul(id
, NULL
, 10);
1092 if (img_id
== cur
->id
)
1101 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "No such image");
1104 grub_env_set(args
[0], cur
->path
);
1106 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
1109 static int ventoy_get_disk_guid(const char *filename
, grub_uint8_t
*guid
)
1116 device_name
= grub_file_get_device_name(filename
);
1128 pos2
= grub_strstr(pos
, ",");
1131 pos2
= grub_strstr(pos
, ")");
1139 disk
= grub_disk_open(pos
);
1142 grub_disk_read(disk
, 0, 0x180, 16, guid
);
1143 grub_disk_close(disk
);
1150 grub_free(device_name
);
1154 grub_uint32_t
ventoy_get_iso_boot_catlog(grub_file_t file
)
1156 eltorito_descriptor desc
;
1158 grub_memset(&desc
, 0, sizeof(desc
));
1159 grub_file_seek(file
, 17 * 2048);
1160 grub_file_read(file
, &desc
, sizeof(desc
));
1162 if (desc
.type
!= 0 || desc
.version
!= 1)
1167 if (grub_strncmp((char *)desc
.id
, "CD001", 5) != 0 ||
1168 grub_strncmp((char *)desc
.system_id
, "EL TORITO SPECIFICATION", 23) != 0)
1176 int ventoy_has_efi_eltorito(grub_file_t file
, grub_uint32_t sector
)
1179 grub_uint8_t buf
[512];
1181 grub_file_seek(file
, sector
* 2048);
1182 grub_file_read(file
, buf
, sizeof(buf
));
1184 if (buf
[0] == 0x01 && buf
[1] == 0xEF)
1186 debug("%s efi eltorito in Validation Entry\n", file
->name
);
1190 for (i
= 64; i
< (int)sizeof(buf
); i
+= 32)
1192 if ((buf
[i
] == 0x90 || buf
[i
] == 0x91) && buf
[i
+ 1] == 0xEF)
1194 debug("%s efi eltorito offset %d 0x%02x\n", file
->name
, i
, buf
[i
]);
1199 debug("%s does not contain efi eltorito\n", file
->name
);
1203 void ventoy_fill_os_param(grub_file_t file
, ventoy_os_param
*param
)
1207 grub_uint8_t chksum
= 0;
1210 disk
= file
->device
->disk
;
1211 grub_memcpy(¶m
->guid
, &g_ventoy_guid
, sizeof(ventoy_guid
));
1213 param
->vtoy_disk_size
= disk
->total_sectors
* (1 << disk
->log_sector_size
);
1214 param
->vtoy_disk_part_id
= disk
->partition
->number
+ 1;
1216 if (grub_strcmp(file
->fs
->name
, "exfat") == 0)
1218 param
->vtoy_disk_part_type
= 0;
1220 else if (grub_strcmp(file
->fs
->name
, "ntfs") == 0)
1222 param
->vtoy_disk_part_type
= 1;
1226 param
->vtoy_disk_part_type
= 0xFFFF;
1229 pos
= grub_strstr(file
->name
, "/");
1235 grub_snprintf(param
->vtoy_img_path
, sizeof(param
->vtoy_img_path
), "%s", pos
);
1237 ventoy_get_disk_guid(file
->name
, param
->vtoy_disk_guid
);
1239 param
->vtoy_img_size
= file
->size
;
1241 param
->vtoy_reserved
[0] = g_ventoy_break_level
;
1242 param
->vtoy_reserved
[1] = g_ventoy_debug_level
;
1244 /* calculate checksum */
1245 for (i
= 0; i
< sizeof(ventoy_os_param
); i
++)
1247 chksum
+= *((grub_uint8_t
*)param
+ i
);
1249 param
->chksum
= (grub_uint8_t
)(0x100 - chksum
);
1254 static grub_err_t
ventoy_cmd_img_sector(grub_extcmd_context_t ctxt
, int argc
, char **args
)
1261 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
1264 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Can't open file %s\n", args
[0]);
1267 if (g_img_chunk_list
.chunk
)
1269 grub_free(g_img_chunk_list
.chunk
);
1272 /* get image chunk data */
1273 grub_memset(&g_img_chunk_list
, 0, sizeof(g_img_chunk_list
));
1274 g_img_chunk_list
.chunk
= grub_malloc(sizeof(ventoy_img_chunk
) * DEFAULT_CHUNK_NUM
);
1275 if (NULL
== g_img_chunk_list
.chunk
)
1277 return grub_error(GRUB_ERR_OUT_OF_MEMORY
, "Can't allocate image chunk memoty\n");
1280 g_img_chunk_list
.max_chunk
= DEFAULT_CHUNK_NUM
;
1281 g_img_chunk_list
.cur_chunk
= 0;
1283 debug("get fat file chunk part start:%llu\n", (unsigned long long)file
->device
->disk
->partition
->start
);
1284 grub_fat_get_file_chunk(file
->device
->disk
->partition
->start
, file
, &g_img_chunk_list
);
1286 grub_file_close(file
);
1288 grub_memset(&g_grub_param
->file_replace
, 0, sizeof(g_grub_param
->file_replace
));
1290 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
1293 static grub_err_t
ventoy_cmd_dump_img_sector(grub_extcmd_context_t ctxt
, int argc
, char **args
)
1296 ventoy_img_chunk
*cur
;
1302 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
1304 cur
= g_img_chunk_list
.chunk
+ i
;
1305 grub_printf("image:[%u - %u] <==> disk:[%llu - %llu]\n",
1306 cur
->img_start_sector
, cur
->img_end_sector
,
1307 (unsigned long long)cur
->disk_start_sector
, (unsigned long long)cur
->disk_end_sector
1311 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
1314 static grub_err_t
ventoy_cmd_add_replace_file(grub_extcmd_context_t ctxt
, int argc
, char **args
)
1317 ventoy_grub_param_file_replace
*replace
= NULL
;
1325 replace
= &(g_grub_param
->file_replace
);
1326 replace
->magic
= GRUB_FILE_REPLACE_MAGIC
;
1328 replace
->old_name_cnt
= 0;
1329 for (i
= 0; i
< 4 && i
+ 1 < argc
; i
++)
1331 replace
->old_name_cnt
++;
1332 grub_snprintf(replace
->old_file_name
[i
], sizeof(replace
->old_file_name
[i
]), "%s", args
[i
+ 1]);
1335 replace
->new_file_virtual_id
= (grub_uint32_t
)grub_strtoul(args
[0], NULL
, 10);
1338 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
1341 static grub_err_t
ventoy_cmd_dump_menu(grub_extcmd_context_t ctxt
, int argc
, char **args
)
1349 grub_printf("List Mode: CurLen:%d MaxLen:%u\n", g_list_script_pos
, VTOY_MAX_SCRIPT_BUF
);
1350 grub_printf("%s", g_list_script_buf
);
1354 grub_printf("Tree Mode: CurLen:%d MaxLen:%u\n", g_tree_script_pos
, VTOY_MAX_SCRIPT_BUF
);
1355 grub_printf("%s", g_tree_script_buf
);
1361 static grub_err_t
ventoy_cmd_check_mode(grub_extcmd_context_t ctxt
, int argc
, char **args
)
1372 if (args
[0][0] == '0')
1374 return g_ventoy_memdisk_mode
? 0 : 1;
1376 else if (args
[0][0] == '1')
1378 return g_ventoy_iso_raw
? 0 : 1;
1380 else if (args
[0][0] == '2')
1382 return g_ventoy_iso_uefi_drv
? 0 : 1;
1388 static grub_err_t
ventoy_cmd_dynamic_menu(grub_extcmd_context_t ctxt
, int argc
, char **args
)
1390 char memfile
[128] = {0};
1398 grub_script_execute_sourcecode(g_list_script_buf
);
1402 g_ventoy_last_entry
= -1;
1403 grub_snprintf(memfile
, sizeof(memfile
), "configfile mem:0x%llx:size:%d",
1404 (ulonglong
)(ulong
)g_tree_script_buf
, g_tree_script_pos
);
1405 grub_script_execute_sourcecode(memfile
);
1411 static grub_err_t
ventoy_cmd_find_bootable_hdd(grub_extcmd_context_t ctxt
, int argc
, char **args
)
1416 const char *isopath
= NULL
;
1418 ventoy_mbr_head mbr
;
1425 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "Usage: %s variable\n", cmd_raw_name
);
1428 isopath
= grub_env_get("iso_path");
1431 debug("isopath is null %p\n", isopath
);
1435 debug("isopath is %s\n", isopath
);
1437 for (id
= 0; id
< 30 && (find
== 0); id
++)
1439 grub_snprintf(hdname
, sizeof(hdname
), "hd%d,", id
);
1440 if (grub_strstr(isopath
, hdname
))
1442 debug("skip %s ...\n", hdname
);
1446 grub_snprintf(hdname
, sizeof(hdname
), "hd%d", id
);
1448 disk
= grub_disk_open(hdname
);
1451 debug("%s not exist\n", hdname
);
1455 grub_memset(&mbr
, 0, sizeof(mbr
));
1456 if (0 == grub_disk_read(disk
, 0, 0, 512, &mbr
))
1458 if (mbr
.Byte55
== 0x55 && mbr
.ByteAA
== 0xAA)
1460 if (mbr
.PartTbl
[0].Active
== 0x80 || mbr
.PartTbl
[1].Active
== 0x80 ||
1461 mbr
.PartTbl
[2].Active
== 0x80 || mbr
.PartTbl
[3].Active
== 0x80)
1464 grub_env_set(args
[0], hdname
);
1468 debug("%s is %s\n", hdname
, find
? "bootable" : "NOT bootable");
1472 debug("read %s failed\n", hdname
);
1475 grub_disk_close(disk
);
1481 grub_file_t
ventoy_grub_file_open(enum grub_file_type type
, const char *fmt
, ...)
1485 char fullpath
[256] = {0};
1488 grub_vsnprintf(fullpath
, 255, fmt
, ap
);
1491 file
= grub_file_open(fullpath
, type
);
1494 debug("grub_file_open failed <%s>\n", fullpath
);
1501 int ventoy_is_file_exist(const char *fmt
, ...)
1506 char buf
[256] = {0};
1508 grub_snprintf(buf
, sizeof(buf
), "%s", "[ -f ");
1512 len
= grub_vsnprintf(pos
, 255, fmt
, ap
);
1515 grub_strncpy(pos
+ len
, " ]", 2);
1517 debug("script exec %s\n", buf
);
1519 if (0 == grub_script_execute_sourcecode(buf
))
1527 static int ventoy_env_init(void)
1531 grub_env_set("vtdebug_flag", "");
1532 grub_env_export("vtdebug_flag");
1534 g_tree_script_buf
= grub_malloc(VTOY_MAX_SCRIPT_BUF
);
1535 g_list_script_buf
= grub_malloc(VTOY_MAX_SCRIPT_BUF
);
1537 ventoy_filt_register(0, ventoy_wrapper_open
);
1539 g_grub_param
= (ventoy_grub_param
*)grub_zalloc(sizeof(ventoy_grub_param
));
1542 g_grub_param
->grub_env_get
= grub_env_get
;
1543 grub_snprintf(buf
, sizeof(buf
), "%p", g_grub_param
);
1544 grub_env_set("env_param", buf
);
1550 static cmd_para ventoy_cmds
[] =
1552 { "vt_incr", ventoy_cmd_incr
, 0, NULL
, "{Var} {INT}", "Increase integer variable", NULL
},
1553 { "vt_debug", ventoy_cmd_debug
, 0, NULL
, "{on|off}", "turn debug on/off", NULL
},
1554 { "vtdebug", ventoy_cmd_debug
, 0, NULL
, "{on|off}", "turn debug on/off", NULL
},
1555 { "vtbreak", ventoy_cmd_break
, 0, NULL
, "{level}", "set debug break", NULL
},
1556 { "vt_cmp", ventoy_cmd_cmp
, 0, NULL
, "{Int1} { eq|ne|gt|lt|ge|le } {Int2}", "Comare two integers", NULL
},
1557 { "vt_device", ventoy_cmd_device
, 0, NULL
, "path var", "", NULL
},
1558 { "vt_check_compatible", ventoy_cmd_check_compatible
, 0, NULL
, "", "", NULL
},
1559 { "vt_list_img", ventoy_cmd_list_img
, 0, NULL
, "{device} {cntvar}", "find all iso file in device", NULL
},
1560 { "vt_clear_img", ventoy_cmd_clear_img
, 0, NULL
, "", "clear image list", NULL
},
1561 { "vt_img_name", ventoy_cmd_img_name
, 0, NULL
, "{imageID} {var}", "get image name", NULL
},
1562 { "vt_chosen_img_path", ventoy_cmd_chosen_img_path
, 0, NULL
, "{var}", "get chosen img path", NULL
},
1563 { "vt_img_sector", ventoy_cmd_img_sector
, 0, NULL
, "{imageName}", "", NULL
},
1564 { "vt_dump_img_sector", ventoy_cmd_dump_img_sector
, 0, NULL
, "", "", NULL
},
1565 { "vt_load_cpio", ventoy_cmd_load_cpio
, 0, NULL
, "", "", NULL
},
1566 { "vt_find_first_bootable_hd", ventoy_cmd_find_bootable_hdd
, 0, NULL
, "", "", NULL
},
1567 { "vt_dump_menu", ventoy_cmd_dump_menu
, 0, NULL
, "", "", NULL
},
1568 { "vt_dynamic_menu", ventoy_cmd_dynamic_menu
, 0, NULL
, "", "", NULL
},
1569 { "vt_check_mode", ventoy_cmd_check_mode
, 0, NULL
, "", "", NULL
},
1571 { "vt_is_udf", ventoy_cmd_is_udf
, 0, NULL
, "", "", NULL
},
1572 { "vt_file_size", ventoy_cmd_file_size
, 0, NULL
, "", "", NULL
},
1573 { "vt_load_iso_to_mem", ventoy_cmd_load_iso_to_mem
, 0, NULL
, "", "", NULL
},
1575 { "vt_linux_parse_initrd_isolinux", ventoy_cmd_isolinux_initrd_collect
, 0, NULL
, "{cfgfile}", "", NULL
},
1576 { "vt_linux_parse_initrd_grub", ventoy_cmd_grub_initrd_collect
, 0, NULL
, "{cfgfile}", "", NULL
},
1577 { "vt_linux_specify_initrd_file", ventoy_cmd_specify_initrd_file
, 0, NULL
, "", "", NULL
},
1578 { "vt_linux_clear_initrd", ventoy_cmd_clear_initrd_list
, 0, NULL
, "", "", NULL
},
1579 { "vt_linux_dump_initrd", ventoy_cmd_dump_initrd_list
, 0, NULL
, "", "", NULL
},
1580 { "vt_linux_initrd_count", ventoy_cmd_initrd_count
, 0, NULL
, "", "", NULL
},
1581 { "vt_linux_valid_initrd_count", ventoy_cmd_valid_initrd_count
, 0, NULL
, "", "", NULL
},
1582 { "vt_linux_locate_initrd", ventoy_cmd_linux_locate_initrd
, 0, NULL
, "", "", NULL
},
1583 { "vt_linux_chain_data", ventoy_cmd_linux_chain_data
, 0, NULL
, "", "", NULL
},
1585 { "vt_windows_reset", ventoy_cmd_wimdows_reset
, 0, NULL
, "", "", NULL
},
1586 { "vt_windows_locate_wim", ventoy_cmd_wimdows_locate_wim
, 0, NULL
, "", "", NULL
},
1587 { "vt_windows_chain_data", ventoy_cmd_windows_chain_data
, 0, NULL
, "", "", NULL
},
1589 { "vt_add_replace_file", ventoy_cmd_add_replace_file
, 0, NULL
, "", "", NULL
},
1592 { "vt_load_plugin", ventoy_cmd_load_plugin
, 0, NULL
, "", "", NULL
},
1597 GRUB_MOD_INIT(ventoy
)
1600 cmd_para
*cur
= NULL
;
1604 for (i
= 0; i
< ARRAY_SIZE(ventoy_cmds
); i
++)
1606 cur
= ventoy_cmds
+ i
;
1607 cur
->cmd
= grub_register_extcmd(cur
->name
, cur
->func
, cur
->flags
,
1608 cur
->summary
, cur
->description
, cur
->parser
);
1612 GRUB_MOD_FINI(ventoy
)
1616 for (i
= 0; i
< ARRAY_SIZE(ventoy_cmds
); i
++)
1618 grub_unregister_extcmd(ventoy_cmds
[i
].cmd
);