/****************************************************************************** * ventoy.c * * Copyright (c) 2020, longpanda * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef GRUB_MACHINE_EFI #include #endif #include #include #include #include "ventoy_def.h" GRUB_MOD_LICENSE ("GPLv3+"); int g_ventoy_debug = 0; static int g_efi_os = 0xFF; initrd_info *g_initrd_img_list = NULL; initrd_info *g_initrd_img_tail = NULL; int g_initrd_img_count = 0; int g_valid_initrd_count = 0; int g_filt_dot_underscore_file = 0; static grub_file_t g_old_file; char g_img_swap_tmp_buf[1024]; img_info g_img_swap_tmp; img_info *g_ventoy_img_list = NULL; int g_ventoy_img_count = 0; grub_device_t g_enum_dev = NULL; grub_fs_t g_enum_fs = NULL; img_iterator_node g_img_iterator_head; img_iterator_node *g_img_iterator_tail = NULL; grub_uint8_t g_ventoy_break_level = 0; grub_uint8_t g_ventoy_debug_level = 0; grub_uint8_t *g_ventoy_cpio_buf = NULL; grub_uint32_t g_ventoy_cpio_size = 0; cpio_newc_header *g_ventoy_initrd_head = NULL; grub_uint8_t *g_ventoy_runtime_buf = NULL; ventoy_grub_param *g_grub_param = NULL; ventoy_guid g_ventoy_guid = VENTOY_GUID; ventoy_img_chunk_list g_img_chunk_list; static char *g_tree_script_buf = NULL; static int g_tree_script_pos = 0; static char *g_list_script_buf = NULL; static int g_list_script_pos = 0; void ventoy_debug(const char *fmt, ...) { va_list args; va_start (args, fmt); grub_vprintf (fmt, args); va_end (args); } int ventoy_is_efi_os(void) { if (g_efi_os > 1) { g_efi_os = (grub_strstr(GRUB_PLATFORM, "efi")) ? 1 : 0; } return g_efi_os; } static int ventoy_get_fs_type(const char *fs) { if (NULL == fs) { return ventoy_fs_max; } else if (grub_strncmp(fs, "exfat", 5) == 0) { return ventoy_fs_exfat; } else if (grub_strncmp(fs, "ntfs", 4) == 0) { return ventoy_fs_ntfs; } else if (grub_strncmp(fs, "ext", 3) == 0) { return ventoy_fs_ext; } else if (grub_strncmp(fs, "xfs", 3) == 0) { return ventoy_fs_xfs; } else if (grub_strncmp(fs, "udf", 3) == 0) { return ventoy_fs_udf; } return ventoy_fs_max; } static int ventoy_string_check(const char *str, grub_char_check_func check) { if (!str) { return 0; } for ( ; *str; str++) { if (!check(*str)) { return 0; } } return 1; } static grub_ssize_t ventoy_fs_read(grub_file_t file, char *buf, grub_size_t len) { grub_memcpy(buf, (char *)file->data + file->offset, len); return len; } static grub_err_t ventoy_fs_close(grub_file_t file) { grub_file_close(g_old_file); grub_free(file->data); file->device = 0; file->name = 0; return 0; } static grub_file_t ventoy_wrapper_open(grub_file_t rawFile, enum grub_file_type type) { int len; grub_file_t file; static struct grub_fs vtoy_fs = { .name = "vtoy", .fs_dir = 0, .fs_open = 0, .fs_read = ventoy_fs_read, .fs_close = ventoy_fs_close, .fs_label = 0, .next = 0 }; if (type != 52) { return rawFile; } file = (grub_file_t)grub_zalloc(sizeof (*file)); if (!file) { return 0; } file->data = grub_malloc(rawFile->size + 4096); if (!file->data) { return 0; } grub_file_read(rawFile, file->data, rawFile->size); len = ventoy_fill_data(4096, (char *)file->data + rawFile->size); g_old_file = rawFile; file->size = rawFile->size + len; file->device = rawFile->device; file->fs = &vtoy_fs; file->not_easily_seekable = 1; return file; } static int ventoy_check_decimal_var(const char *name, long *value) { const char *value_str = NULL; value_str = grub_env_get(name); if (NULL == value_str) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Variable %s not found", name); } if (!ventoy_is_decimal(value_str)) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Variable %s value '%s' is not an integer", name, value_str); } *value = grub_strtol(value_str, NULL, 10); return GRUB_ERR_NONE; } static grub_err_t ventoy_cmd_debug(grub_extcmd_context_t ctxt, int argc, char **args) { if (argc != 1) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {on|off}", cmd_raw_name); } if (0 == grub_strcmp(args[0], "on")) { g_ventoy_debug = 1; grub_env_set("vtdebug_flag", "debug"); } else { g_ventoy_debug = 0; grub_env_set("vtdebug_flag", ""); } VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_err_t ventoy_cmd_break(grub_extcmd_context_t ctxt, int argc, char **args) { (void)ctxt; if (argc < 1 || (args[0][0] != '0' && args[0][0] != '1')) { grub_printf("Usage: %s {level} [debug]\r\n", cmd_raw_name); grub_printf(" level:\r\n"); grub_printf(" 01/11: busybox / (+cat log)\r\n"); grub_printf(" 02/12: initrd / (+cat log)\r\n"); grub_printf(" 03/13: hook / (+cat log)\r\n"); grub_printf("\r\n"); grub_printf(" debug:\r\n"); grub_printf(" 0: debug is on\r\n"); grub_printf(" 1: debug is off\r\n"); grub_printf("\r\n"); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } g_ventoy_break_level = (grub_uint8_t)grub_strtoul(args[0], NULL, 16); if (argc > 1 && grub_strtoul(args[1], NULL, 10) > 0) { g_ventoy_debug_level = 1; } VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_err_t ventoy_cmd_incr(grub_extcmd_context_t ctxt, int argc, char **args) { long value_long = 0; char buf[32]; if ((argc != 2) || (!ventoy_is_decimal(args[1]))) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Variable} {Int}", cmd_raw_name); } if (GRUB_ERR_NONE != ventoy_check_decimal_var(args[0], &value_long)) { return grub_errno; } value_long += grub_strtol(args[1], NULL, 10); grub_snprintf(buf, sizeof(buf), "%ld", value_long); grub_env_set(args[0], buf); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_err_t ventoy_cmd_file_size(grub_extcmd_context_t ctxt, int argc, char **args) { int rc = 1; char buf[32]; grub_file_t file; (void)ctxt; (void)argc; (void)args; if (argc != 2) { return rc; } file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); if (file == NULL) { debug("failed to open file <%s> for udf check\n", args[0]); return 1; } grub_snprintf(buf, sizeof(buf), "%llu", (unsigned long long)file->size); grub_env_set(args[1], buf); grub_file_close(file); rc = 0; return rc; } static grub_err_t ventoy_cmd_load_iso_to_mem(grub_extcmd_context_t ctxt, int argc, char **args) { int rc = 1; char name[32]; char value[32]; char *buf = NULL; grub_file_t file; (void)ctxt; (void)argc; (void)args; if (argc != 2) { return rc; } file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); if (file == NULL) { debug("failed to open file <%s> for udf check\n", args[0]); return 1; } #ifdef GRUB_MACHINE_EFI buf = (char *)grub_efi_allocate_iso_buf(file->size); #else buf = (char *)grub_malloc(file->size); #endif grub_file_read(file, buf, file->size); grub_snprintf(name, sizeof(name), "%s_addr", args[1]); grub_snprintf(value, sizeof(value), "0x%llx", (unsigned long long)(unsigned long)buf); grub_env_set(name, value); grub_snprintf(name, sizeof(name), "%s_size", args[1]); grub_snprintf(value, sizeof(value), "%llu", (unsigned long long)file->size); grub_env_set(name, value); grub_file_close(file); rc = 0; return rc; } static grub_err_t ventoy_cmd_is_udf(grub_extcmd_context_t ctxt, int argc, char **args) { int i; int rc = 1; grub_file_t file; grub_uint8_t buf[32]; (void)ctxt; (void)argc; (void)args; if (argc != 1) { return rc; } file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); if (file == NULL) { debug("failed to open file <%s> for udf check\n", args[0]); return 1; } for (i = 16; i < 32; i++) { grub_file_seek(file, i * 2048); grub_file_read(file, buf, sizeof(buf)); if (buf[0] == 255) { break; } } i++; grub_file_seek(file, i * 2048); grub_file_read(file, buf, sizeof(buf)); if (grub_memcmp(buf + 1, "BEA01", 5) == 0) { i++; grub_file_seek(file, i * 2048); grub_file_read(file, buf, sizeof(buf)); if (grub_memcmp(buf + 1, "NSR02", 5) == 0 || grub_memcmp(buf + 1, "NSR03", 5) == 0) { rc = 0; } } grub_file_close(file); debug("ISO UDF: %s\n", rc ? "NO" : "YES"); return rc; } static grub_err_t ventoy_cmd_cmp(grub_extcmd_context_t ctxt, int argc, char **args) { long value_long1 = 0; long value_long2 = 0; if ((argc != 3) || (!ventoy_is_decimal(args[0])) || (!ventoy_is_decimal(args[2]))) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Int1} { eq|ne|gt|lt|ge|le } {Int2}", cmd_raw_name); } value_long1 = grub_strtol(args[0], NULL, 10); value_long2 = grub_strtol(args[2], NULL, 10); if (0 == grub_strcmp(args[1], "eq")) { grub_errno = (value_long1 == value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; } else if (0 == grub_strcmp(args[1], "ne")) { grub_errno = (value_long1 != value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; } else if (0 == grub_strcmp(args[1], "gt")) { grub_errno = (value_long1 > value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; } else if (0 == grub_strcmp(args[1], "lt")) { grub_errno = (value_long1 < value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; } else if (0 == grub_strcmp(args[1], "ge")) { grub_errno = (value_long1 >= value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; } else if (0 == grub_strcmp(args[1], "le")) { grub_errno = (value_long1 <= value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; } else { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Int1} { eq ne gt lt ge le } {Int2}", cmd_raw_name); } return grub_errno; } static grub_err_t ventoy_cmd_device(grub_extcmd_context_t ctxt, int argc, char **args) { char *pos = NULL; char buf[128] = {0}; if (argc != 2) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s path var", cmd_raw_name); } grub_strncpy(buf, (args[0][0] == '(') ? args[0] + 1 : args[0], sizeof(buf) - 1); pos = grub_strstr(buf, ","); if (pos) { *pos = 0; } grub_env_set(args[1], buf); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_err_t ventoy_cmd_check_compatible(grub_extcmd_context_t ctxt, int argc, char **args) { int i; char buf[256]; grub_disk_t disk; char *pos = NULL; const char *files[] = { "ventoy.dat", "VENTOY.DAT" }; (void)ctxt; if (argc != 1) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s (loop)", cmd_raw_name); } for (i = 0; i < (int)ARRAY_SIZE(files); i++) { grub_snprintf(buf, sizeof(buf) - 1, "[ -e %s/%s ]", args[0], files[i]); if (0 == grub_script_execute_sourcecode(buf)) { debug("file %s exist, ventoy_compatible YES\n", buf); grub_env_set("ventoy_compatible", "YES"); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } else { debug("file %s NOT exist\n", buf); } } grub_snprintf(buf, sizeof(buf) - 1, "%s", args[0][0] == '(' ? (args[0] + 1) : args[0]); pos = grub_strstr(buf, ")"); if (pos) { *pos = 0; } disk = grub_disk_open(buf); if (disk) { grub_disk_read(disk, 16 << 2, 0, 1024, g_img_swap_tmp_buf); grub_disk_close(disk); g_img_swap_tmp_buf[703] = 0; for (i = 319; i < 703; i++) { if (g_img_swap_tmp_buf[i] == 'V' && 0 == grub_strncmp(g_img_swap_tmp_buf + i, VENTOY_COMPATIBLE_STR, VENTOY_COMPATIBLE_STR_LEN)) { debug("Ventoy compatible string exist at %d, ventoy_compatible YES\n", i); grub_env_set("ventoy_compatible", "YES"); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } } } else { debug("failed to open disk <%s>\n", buf); } grub_env_set("ventoy_compatible", "NO"); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } int ventoy_cmp_img(img_info *img1, img_info *img2) { char *s1, *s2; int c1 = 0; int c2 = 0; for (s1 = img1->name, s2 = img2->name; *s1 && *s2; s1++, s2++) { c1 = *s1; c2 = *s2; if (grub_islower(c1)) { c1 = c1 - 'a' + 'A'; } if (grub_islower(c2)) { c2 = c2 - 'a' + 'A'; } if (c1 != c2) { break; } } return (c1 - c2); } void ventoy_swap_img(img_info *img1, img_info *img2) { grub_memcpy(&g_img_swap_tmp, img1, sizeof(img_info)); grub_memcpy(img1, img2, sizeof(img_info)); img1->next = g_img_swap_tmp.next; img1->prev = g_img_swap_tmp.prev; g_img_swap_tmp.next = img2->next; g_img_swap_tmp.prev = img2->prev; grub_memcpy(img2, &g_img_swap_tmp, sizeof(img_info)); } static int ventoy_img_name_valid(const char *filename, grub_size_t namelen) { grub_size_t i; if (g_filt_dot_underscore_file && filename[0] == '.' && filename[1] == '_') { return 0; } for (i = 0; i < namelen; i++) { if (filename[i] == ' ' || filename[i] == '\t') { return 0; } if ((grub_uint8_t)(filename[i]) >= 127) { return 0; } } return 1; } static int ventoy_check_ignore_flag(const char *filename, const struct grub_dirhook_info *info, void *data) { if (0 == info->dir) { if (filename && filename[0] == '.' && 0 == grub_strncmp(filename, ".ventoyignore", 13)) { *((int *)data) = 1; return 0; } } return 0; } static int ventoy_colect_img_files(const char *filename, const struct grub_dirhook_info *info, void *data) { int ignore = 0; grub_size_t len; img_info *img; img_info *tail; img_iterator_node *tmp; img_iterator_node *new_node; img_iterator_node *node = (img_iterator_node *)data; len = grub_strlen(filename); if (info->dir) { if ((len == 1 && filename[0] == '.') || (len == 2 && filename[0] == '.' && filename[1] == '.')) { return 0; } if (!ventoy_img_name_valid(filename, len)) { return 0; } if (filename[0] == '$' && 0 == grub_strncmp(filename, "$RECYCLE.BIN", 12)) { return 0; } new_node = grub_zalloc(sizeof(img_iterator_node)); if (new_node) { new_node->dirlen = grub_snprintf(new_node->dir, sizeof(new_node->dir), "%s%s/", node->dir, filename); g_enum_fs->fs_dir(g_enum_dev, new_node->dir, ventoy_check_ignore_flag, &ignore); if (ignore) { debug("Directory %s ignored...\n", new_node->dir); grub_free(new_node); return 0; } new_node->tail = node->tail; new_node->parent = node; if (!node->firstchild) { node->firstchild = new_node; } if (g_img_iterator_tail) { g_img_iterator_tail->next = new_node; g_img_iterator_tail = new_node; } else { g_img_iterator_head.next = new_node; g_img_iterator_tail = new_node; } } } else { debug("Find a file %s\n", filename); if ((len > 4) && (0 == grub_strcasecmp(filename + len - 4, ".iso"))) { if (!ventoy_img_name_valid(filename, len)) { return 0; } img = grub_zalloc(sizeof(img_info)); if (img) { grub_snprintf(img->name, sizeof(img->name), "%s", filename); grub_snprintf(img->path, sizeof(img->path), "%s%s", node->dir, filename); if (g_ventoy_img_list) { tail = *(node->tail); img->prev = tail; tail->next = img; } else { g_ventoy_img_list = img; } img->size = info->size; img->id = g_ventoy_img_count; img->parent = node; if (node && NULL == node->firstiso) { node->firstiso = img; } node->isocnt++; tmp = node->parent; while (tmp) { tmp->isocnt++; tmp = tmp->parent; } *((img_info **)(node->tail)) = img; g_ventoy_img_count++; debug("Add %s%s to list %d\n", node->dir, filename, g_ventoy_img_count); } } } return 0; } int ventoy_fill_data(grub_uint32_t buflen, char *buffer) { int len = GRUB_UINT_MAX; const char *value = NULL; char name[32] = {0}; char plat[32] = {0}; char guidstr[32] = {0}; ventoy_guid guid = VENTOY_GUID; const char *fmt1 = NULL; const char *fmt2 = NULL; const char *fmt3 = NULL; grub_uint32_t *puint = (grub_uint32_t *)name; grub_uint32_t *puint2 = (grub_uint32_t *)plat; const char fmtdata[]={ 0x39, 0x35, 0x25, 0x00, 0x35, 0x00, 0x23, 0x30, 0x30, 0x30, 0x30, 0x66, 0x66, 0x00 }; const char fmtcode[]={ 0x22, 0x0A, 0x2B, 0x20, 0x68, 0x62, 0x6F, 0x78, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x74, 0x6F, 0x70, 0x20, 0x3D, 0x20, 0x25, 0x73, 0x0A, 0x20, 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x3D, 0x20, 0x25, 0x73, 0x0A, 0x20, 0x20, 0x2B, 0x20, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x20, 0x7B, 0x74, 0x65, 0x78, 0x74, 0x20, 0x3D, 0x20, 0x22, 0x25, 0x73, 0x20, 0x25, 0x73, 0x25, 0x73, 0x22, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x20, 0x3D, 0x20, 0x22, 0x25, 0x73, 0x22, 0x20, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x20, 0x3D, 0x20, 0x22, 0x6C, 0x65, 0x66, 0x74, 0x22, 0x7D, 0x0A, 0x7D, 0x0A, 0x22, 0x00 }; grub_memset(name, 0, sizeof(name)); puint[0] = grub_swap_bytes32(0x56454e54); puint[3] = grub_swap_bytes32(0x4f4e0000); puint[2] = grub_swap_bytes32(0x45525349); puint[1] = grub_swap_bytes32(0x4f595f56); value = ventoy_get_env(name); grub_memset(name, 0, sizeof(name)); puint[1] = grub_swap_bytes32(0x5f544f50); puint[0] = grub_swap_bytes32(0x56544c45); fmt1 = ventoy_get_env(name); if (!fmt1) { fmt1 = fmtdata; } grub_memset(name, 0, sizeof(name)); puint[1] = grub_swap_bytes32(0x5f4c4654); puint[0] = grub_swap_bytes32(0x56544c45); fmt2 = ventoy_get_env(name); grub_memset(name, 0, sizeof(name)); puint[1] = grub_swap_bytes32(0x5f434c52); puint[0] = grub_swap_bytes32(0x56544c45); fmt3 = ventoy_get_env(name); grub_memcpy(guidstr, &guid, sizeof(guid)); #if defined (GRUB_MACHINE_EFI) puint2[0] = grub_swap_bytes32(0x55454649); #else puint2[0] = grub_swap_bytes32(0x42494f53); #endif /* Easter egg :) It will be appreciated if you reserve it, but NOT mandatory. */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" len = grub_snprintf(buffer, buflen, fmtcode, fmt1 ? fmt1 : fmtdata, fmt2 ? fmt2 : fmtdata + 4, value ? value : "", plat, guidstr, fmt3 ? fmt3 : fmtdata + 6); #pragma GCC diagnostic pop grub_memset(name, 0, sizeof(name)); puint[0] = grub_swap_bytes32(0x76746f79); puint[2] = grub_swap_bytes32(0x656e7365); puint[1] = grub_swap_bytes32(0x5f6c6963); ventoy_set_env(name, guidstr); return len; } static img_info * ventoy_get_min_iso(img_iterator_node *node) { img_info *minimg = NULL; img_info *img = (img_info *)(node->firstiso); while (img && (img_iterator_node *)(img->parent) == node) { if (img->select == 0 && (NULL == minimg || grub_strcmp(img->name, minimg->name) < 0)) { minimg = img; } img = img->next; } if (minimg) { minimg->select = 1; } return minimg; } static img_iterator_node * ventoy_get_min_child(img_iterator_node *node) { img_iterator_node *Minchild = NULL; img_iterator_node *child = node->firstchild; while (child && child->parent == node) { if (child->select == 0 && (NULL == Minchild || grub_strcmp(child->dir, Minchild->dir) < 0)) { Minchild = child; } child = child->next; } if (Minchild) { Minchild->select = 1; } return Minchild; } static int ventoy_dynamic_tree_menu(img_iterator_node *node) { int offset = 1; img_info *img; img_iterator_node *child = NULL; if (node->isocnt == 0 || node->done == 1) { return 0; } if (node->parent && node->parent->dirlen < node->dirlen) { offset = node->parent->dirlen; } if (node != &g_img_iterator_head) { node->dir[node->dirlen - 1] = 0; g_tree_script_pos += grub_snprintf(g_tree_script_buf + g_tree_script_pos, VTOY_MAX_SCRIPT_BUF - g_tree_script_pos, "submenu \"%-10s [%s]\" {\n", "DIR", node->dir + offset); } while ((child = ventoy_get_min_child(node)) != NULL) { ventoy_dynamic_tree_menu(child); } while ((img = ventoy_get_min_iso(node)) != NULL) { g_tree_script_pos += grub_snprintf(g_tree_script_buf + g_tree_script_pos, VTOY_MAX_SCRIPT_BUF - g_tree_script_pos, "menuentry \"%-10s %s\" --id=\"VID_%d\" {\n" " common_menuentry \n" "}\n", grub_get_human_size(img->size, GRUB_HUMAN_SIZE_SHORT), img->name, img->id); } if (node != &g_img_iterator_head) { g_tree_script_pos += grub_snprintf(g_tree_script_buf + g_tree_script_pos, VTOY_MAX_SCRIPT_BUF - g_tree_script_pos, "}\n"); } node->done = 1; return 0; } static grub_err_t ventoy_cmd_list_img(grub_extcmd_context_t ctxt, int argc, char **args) { grub_fs_t fs; grub_device_t dev = NULL; img_info *cur = NULL; img_info *tail = NULL; const char *strdata = NULL; char *device_name = NULL; char buf[32]; img_iterator_node *node = NULL; img_iterator_node *tmp = NULL; (void)ctxt; if (argc != 2) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {device} {cntvar}", cmd_raw_name); } if (g_ventoy_img_list || g_ventoy_img_count) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Must clear image before list"); } strdata = ventoy_get_env("VTOY_FILT_DOT_UNDERSCORE_FILE"); if (strdata && strdata[0] == '1' && strdata[1] == 0) { g_filt_dot_underscore_file = 1; } device_name = grub_file_get_device_name(args[0]); if (!device_name) { goto fail; } g_enum_dev = dev = grub_device_open(device_name); if (!dev) { goto fail; } g_enum_fs = fs = grub_fs_probe(dev); if (!fs) { goto fail; } if (ventoy_get_fs_type(fs->name) >= ventoy_fs_max) { debug("unsupported fs:<%s>\n", fs->name); goto fail; } grub_memset(&g_img_iterator_head, 0, sizeof(g_img_iterator_head)); g_img_iterator_head.dirlen = 1; g_img_iterator_head.tail = &tail; grub_strcpy(g_img_iterator_head.dir, "/"); for (node = &g_img_iterator_head; node; node = node->next) { fs->fs_dir(dev, node->dir, ventoy_colect_img_files, node); } for (node = &g_img_iterator_head; node; node = node->next) { ventoy_dynamic_tree_menu(node); } /* free node */ node = g_img_iterator_head.next; while (node) { tmp = node->next; grub_free(node); node = tmp; } /* sort image list by image name */ for (cur = g_ventoy_img_list; cur; cur = cur->next) { for (tail = cur->next; tail; tail = tail->next) { if (ventoy_cmp_img(cur, tail) > 0) { ventoy_swap_img(cur, tail); } } } for (cur = g_ventoy_img_list; cur; cur = cur->next) { g_list_script_pos += grub_snprintf(g_list_script_buf + g_list_script_pos, VTOY_MAX_SCRIPT_BUF - g_list_script_pos, "menuentry \"%s\" --id=\"VID_%d\" {\n" " common_menuentry \n" "}\n", cur->name, cur->id); } g_list_script_buf[g_list_script_pos] = 0; grub_snprintf(buf, sizeof(buf), "%d", g_ventoy_img_count); grub_env_set(args[1], buf); fail: check_free(device_name, grub_free); check_free(dev, grub_device_close); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_err_t ventoy_cmd_clear_img(grub_extcmd_context_t ctxt, int argc, char **args) { img_info *next = NULL; img_info *cur = g_ventoy_img_list; (void)ctxt; (void)argc; (void)args; while (cur) { next = cur->next; grub_free(cur); cur = next; } g_ventoy_img_list = NULL; g_ventoy_img_count = 0; VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_err_t ventoy_cmd_img_name(grub_extcmd_context_t ctxt, int argc, char **args) { long img_id = 0; img_info *cur = g_ventoy_img_list; (void)ctxt; if (argc != 2 || (!ventoy_is_decimal(args[0]))) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {imageID} {var}", cmd_raw_name); } img_id = grub_strtol(args[0], NULL, 10); if (img_id >= g_ventoy_img_count) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such many images %ld %ld", img_id, g_ventoy_img_count); } debug("Find image %ld name \n", img_id); while (cur && img_id > 0) { img_id--; cur = cur->next; } if (!cur) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such many images"); } debug("image name is %s\n", cur->name); grub_env_set(args[1], cur->name); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_err_t ventoy_cmd_chosen_img_path(grub_extcmd_context_t ctxt, int argc, char **args) { int img_id = 0; char *pos = NULL; const char *id = NULL; img_info *cur = g_ventoy_img_list; (void)ctxt; if (argc != 1) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {var}", cmd_raw_name); } id = grub_env_get("chosen"); pos = grub_strstr(id, "VID_"); if (pos) { img_id = (int)grub_strtoul(pos + 4, NULL, 10); } else { img_id = (int)grub_strtoul(id, NULL, 10); } while (cur) { if (img_id == cur->id) { break; } cur = cur->next; } if (!cur) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such image"); } grub_env_set(args[0], cur->path); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static int ventoy_get_disk_guid(const char *filename, grub_uint8_t *guid) { grub_disk_t disk; char *device_name; char *pos; char *pos2; device_name = grub_file_get_device_name(filename); if (!device_name) { return 1; } pos = device_name; if (pos[0] == '(') { pos++; } pos2 = grub_strstr(pos, ","); if (!pos2) { pos2 = grub_strstr(pos, ")"); } if (pos2) { *pos2 = 0; } disk = grub_disk_open(pos); if (disk) { grub_disk_read(disk, 0, 0x180, 16, guid); grub_disk_close(disk); } else { return 1; } grub_free(device_name); return 0; } grub_uint32_t ventoy_get_iso_boot_catlog(grub_file_t file) { eltorito_descriptor desc; grub_memset(&desc, 0, sizeof(desc)); grub_file_seek(file, 17 * 2048); grub_file_read(file, &desc, sizeof(desc)); if (desc.type != 0 || desc.version != 1) { return 0; } if (grub_strncmp((char *)desc.id, "CD001", 5) != 0 || grub_strncmp((char *)desc.system_id, "EL TORITO SPECIFICATION", 23) != 0) { return 0; } return desc.sector; } int ventoy_has_efi_eltorito(grub_file_t file, grub_uint32_t sector) { int i; grub_uint8_t buf[512]; grub_file_seek(file, sector * 2048); grub_file_read(file, buf, sizeof(buf)); if (buf[0] == 0x01 && buf[1] == 0xEF) { debug("%s efi eltorito in Validation Entry\n", file->name); return 1; } for (i = 64; i < (int)sizeof(buf); i += 32) { if ((buf[i] == 0x90 || buf[i] == 0x91) && buf[i + 1] == 0xEF) { debug("%s efi eltorito offset %d 0x%02x\n", file->name, i, buf[i]); return 1; } } debug("%s does not contain efi eltorito\n", file->name); return 0; } void ventoy_fill_os_param(grub_file_t file, ventoy_os_param *param) { char *pos; grub_uint32_t i; grub_uint8_t chksum = 0; grub_disk_t disk; disk = file->device->disk; grub_memcpy(¶m->guid, &g_ventoy_guid, sizeof(ventoy_guid)); param->vtoy_disk_size = disk->total_sectors * (1 << disk->log_sector_size); param->vtoy_disk_part_id = disk->partition->number + 1; param->vtoy_disk_part_type = ventoy_get_fs_type(file->fs->name); pos = grub_strstr(file->name, "/"); if (!pos) { pos = file->name; } grub_snprintf(param->vtoy_img_path, sizeof(param->vtoy_img_path), "%s", pos); ventoy_get_disk_guid(file->name, param->vtoy_disk_guid); param->vtoy_img_size = file->size; param->vtoy_reserved[0] = g_ventoy_break_level; param->vtoy_reserved[1] = g_ventoy_debug_level; /* calculate checksum */ for (i = 0; i < sizeof(ventoy_os_param); i++) { chksum += *((grub_uint8_t *)param + i); } param->chksum = (grub_uint8_t)(0x100 - chksum); return; } static int ventoy_get_block_list(grub_file_t file, ventoy_img_chunk_list *chunklist, grub_disk_addr_t start) { int fs_type; grub_uint32_t i = 0; grub_uint32_t sector = 0; grub_uint32_t count = 0; grub_off_t size = 0; grub_off_t read = 0; fs_type = ventoy_get_fs_type(file->fs->name); if (fs_type == ventoy_fs_exfat) { grub_fat_get_file_chunk(start, file, chunklist); } else { file->read_hook = (grub_disk_read_hook_t)grub_disk_blocklist_read; file->read_hook_data = chunklist; for (size = file->size; size > 0; size -= read) { read = (size > VTOY_SIZE_1GB) ? VTOY_SIZE_1GB : size; grub_file_read(file, NULL, read); } for (i = 0; start > 0 && i < chunklist->cur_chunk; i++) { chunklist->chunk[i].disk_start_sector += start; chunklist->chunk[i].disk_end_sector += start; } if (ventoy_fs_udf == fs_type) { for (i = 0; i < chunklist->cur_chunk; i++) { count = (chunklist->chunk[i].disk_end_sector + 1 - chunklist->chunk[i].disk_start_sector) >> 2; chunklist->chunk[i].img_start_sector = sector; chunklist->chunk[i].img_end_sector = sector + count - 1; sector += count; } } } return 0; } static grub_err_t ventoy_cmd_img_sector(grub_extcmd_context_t ctxt, int argc, char **args) { grub_file_t file; (void)ctxt; (void)argc; file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); if (!file) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]); } if (g_img_chunk_list.chunk) { grub_free(g_img_chunk_list.chunk); } /* get image chunk data */ grub_memset(&g_img_chunk_list, 0, sizeof(g_img_chunk_list)); g_img_chunk_list.chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM); if (NULL == g_img_chunk_list.chunk) { return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n"); } g_img_chunk_list.max_chunk = DEFAULT_CHUNK_NUM; g_img_chunk_list.cur_chunk = 0; ventoy_get_block_list(file, &g_img_chunk_list, file->device->disk->partition->start); grub_file_close(file); grub_memset(&g_grub_param->file_replace, 0, sizeof(g_grub_param->file_replace)); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_err_t ventoy_cmd_dump_img_sector(grub_extcmd_context_t ctxt, int argc, char **args) { grub_uint32_t i; ventoy_img_chunk *cur; (void)ctxt; (void)argc; (void)args; for (i = 0; i < g_img_chunk_list.cur_chunk; i++) { cur = g_img_chunk_list.chunk + i; grub_printf("image:[%u - %u] <==> disk:[%llu - %llu]\n", cur->img_start_sector, cur->img_end_sector, (unsigned long long)cur->disk_start_sector, (unsigned long long)cur->disk_end_sector ); } VENTOY_CMD_RETURN(GRUB_ERR_NONE); } #ifdef GRUB_MACHINE_EFI static grub_err_t ventoy_cmd_relocator_chaindata(grub_extcmd_context_t ctxt, int argc, char **args) { (void)ctxt; (void)argc; (void)args; return 0; } #else static grub_err_t ventoy_cmd_relocator_chaindata(grub_extcmd_context_t ctxt, int argc, char **args) { int rc = 0; ulong chain_len = 0; char *chain_data = NULL; char *relocator_addr = NULL; grub_relocator_chunk_t ch; struct grub_relocator *relocator = NULL; char envbuf[64] = { 0 }; (void)ctxt; (void)argc; (void)args; if (argc != 2) { return 1; } chain_data = (char *)grub_strtoul(args[0], NULL, 16); chain_len = grub_strtoul(args[1], NULL, 10); relocator = grub_relocator_new (); if (!relocator) { debug("grub_relocator_new failed %p %lu\n", chain_data, chain_len); return 1; } rc = grub_relocator_alloc_chunk_addr (relocator, &ch, 0x100000, // GRUB_LINUX_BZIMAGE_ADDR, chain_len); if (rc) { debug("grub_relocator_alloc_chunk_addr failed %d %p %lu\n", rc, chain_data, chain_len); grub_relocator_unload (relocator); return 1; } relocator_addr = get_virtual_current_address(ch); grub_memcpy(relocator_addr, chain_data, chain_len); grub_relocator_unload (relocator); grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)relocator_addr); grub_env_set("vtoy_chain_relocator_addr", envbuf); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } #endif static grub_err_t ventoy_cmd_test_block_list(grub_extcmd_context_t ctxt, int argc, char **args) { grub_uint32_t i; grub_file_t file; ventoy_img_chunk_list chunklist; (void)ctxt; (void)argc; file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); if (!file) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]); } /* get image chunk data */ grub_memset(&chunklist, 0, sizeof(chunklist)); chunklist.chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM); if (NULL == chunklist.chunk) { return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n"); } chunklist.max_chunk = DEFAULT_CHUNK_NUM; chunklist.cur_chunk = 0; ventoy_get_block_list(file, &chunklist, 0); grub_file_close(file); grub_printf("filesystem: <%s> entry number:<%u>\n", file->fs->name, chunklist.cur_chunk); for (i = 0; i < chunklist.cur_chunk; i++) { grub_printf("%llu+%llu,", (ulonglong)chunklist.chunk[i].disk_start_sector, (ulonglong)(chunklist.chunk[i].disk_end_sector + 1 - chunklist.chunk[i].disk_start_sector)); } grub_printf("\n==================================\n"); for (i = 0; i < chunklist.cur_chunk; i++) { grub_printf("%2u: [%llu %llu] - [%llu %llu]\n", i, (ulonglong)chunklist.chunk[i].img_start_sector, (ulonglong)chunklist.chunk[i].img_end_sector, (ulonglong)chunklist.chunk[i].disk_start_sector, (ulonglong)chunklist.chunk[i].disk_end_sector ); } grub_free(chunklist.chunk); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_err_t ventoy_cmd_add_replace_file(grub_extcmd_context_t ctxt, int argc, char **args) { int i; ventoy_grub_param_file_replace *replace = NULL; (void)ctxt; (void)argc; (void)args; if (argc >= 2) { replace = &(g_grub_param->file_replace); replace->magic = GRUB_FILE_REPLACE_MAGIC; replace->old_name_cnt = 0; for (i = 0; i < 4 && i + 1 < argc; i++) { replace->old_name_cnt++; grub_snprintf(replace->old_file_name[i], sizeof(replace->old_file_name[i]), "%s", args[i + 1]); } replace->new_file_virtual_id = (grub_uint32_t)grub_strtoul(args[0], NULL, 10); } VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_err_t ventoy_cmd_dump_menu(grub_extcmd_context_t ctxt, int argc, char **args) { (void)ctxt; (void)argc; (void)args; if (argc == 0) { grub_printf("List Mode: CurLen:%d MaxLen:%u\n", g_list_script_pos, VTOY_MAX_SCRIPT_BUF); grub_printf("%s", g_list_script_buf); } else { grub_printf("Tree Mode: CurLen:%d MaxLen:%u\n", g_tree_script_pos, VTOY_MAX_SCRIPT_BUF); grub_printf("%s", g_tree_script_buf); } return 0; } static grub_err_t ventoy_cmd_dump_auto_install(grub_extcmd_context_t ctxt, int argc, char **args) { (void)ctxt; (void)argc; (void)args; ventoy_plugin_dump_auto_install(); return 0; } static grub_err_t ventoy_cmd_check_mode(grub_extcmd_context_t ctxt, int argc, char **args) { (void)ctxt; (void)argc; (void)args; if (argc != 1) { return 1; } if (args[0][0] == '0') { return g_ventoy_memdisk_mode ? 0 : 1; } else if (args[0][0] == '1') { return g_ventoy_iso_raw ? 0 : 1; } else if (args[0][0] == '2') { return g_ventoy_iso_uefi_drv ? 0 : 1; } return 1; } static grub_err_t ventoy_cmd_dynamic_menu(grub_extcmd_context_t ctxt, int argc, char **args) { static int configfile_mode = 0; char memfile[128] = {0}; (void)ctxt; (void)argc; (void)args; /* * args[0]: 0:normal 1:configfile * args[1]: 0:list_buf 1:tree_buf */ if (argc != 2) { debug("Invalid argc %d\n", argc); return 0; } if (args[0][0] == '0') { if (args[1][0] == '0') { grub_script_execute_sourcecode(g_list_script_buf); } else { grub_script_execute_sourcecode(g_tree_script_buf); } } else { if (configfile_mode) { debug("Now already in F3 mode %d\n", configfile_mode); return 0; } if (args[1][0] == '0') { grub_snprintf(memfile, sizeof(memfile), "configfile mem:0x%llx:size:%d", (ulonglong)(ulong)g_list_script_buf, g_list_script_pos); } else { g_ventoy_last_entry = -1; grub_snprintf(memfile, sizeof(memfile), "configfile mem:0x%llx:size:%d", (ulonglong)(ulong)g_tree_script_buf, g_tree_script_pos); } configfile_mode = 1; grub_script_execute_sourcecode(memfile); configfile_mode = 0; } return 0; } static grub_err_t ventoy_cmd_find_bootable_hdd(grub_extcmd_context_t ctxt, int argc, char **args) { int id = 0; int find = 0; grub_disk_t disk; const char *isopath = NULL; char hdname[32]; ventoy_mbr_head mbr; (void)ctxt; (void)argc; if (argc != 1) { return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s variable\n", cmd_raw_name); } isopath = grub_env_get("iso_path"); if (!isopath) { debug("isopath is null %p\n", isopath); return 0; } debug("isopath is %s\n", isopath); for (id = 0; id < 30 && (find == 0); id++) { grub_snprintf(hdname, sizeof(hdname), "hd%d,", id); if (grub_strstr(isopath, hdname)) { debug("skip %s ...\n", hdname); continue; } grub_snprintf(hdname, sizeof(hdname), "hd%d", id); disk = grub_disk_open(hdname); if (!disk) { debug("%s not exist\n", hdname); break; } grub_memset(&mbr, 0, sizeof(mbr)); if (0 == grub_disk_read(disk, 0, 0, 512, &mbr)) { if (mbr.Byte55 == 0x55 && mbr.ByteAA == 0xAA) { if (mbr.PartTbl[0].Active == 0x80 || mbr.PartTbl[1].Active == 0x80 || mbr.PartTbl[2].Active == 0x80 || mbr.PartTbl[3].Active == 0x80) { grub_env_set(args[0], hdname); find = 1; } } debug("%s is %s\n", hdname, find ? "bootable" : "NOT bootable"); } else { debug("read %s failed\n", hdname); } grub_disk_close(disk); } return 0; } grub_file_t ventoy_grub_file_open(enum grub_file_type type, const char *fmt, ...) { va_list ap; grub_file_t file; char fullpath[256] = {0}; va_start (ap, fmt); grub_vsnprintf(fullpath, 255, fmt, ap); va_end (ap); file = grub_file_open(fullpath, type); if (!file) { debug("grub_file_open failed <%s>\n", fullpath); grub_errno = 0; } return file; } int ventoy_is_file_exist(const char *fmt, ...) { va_list ap; int len; char *pos = NULL; char buf[256] = {0}; grub_snprintf(buf, sizeof(buf), "%s", "[ -f "); pos = buf + 5; va_start (ap, fmt); len = grub_vsnprintf(pos, 255, fmt, ap); va_end (ap); grub_strncpy(pos + len, " ]", 2); debug("script exec %s\n", buf); if (0 == grub_script_execute_sourcecode(buf)) { return 1; } return 0; } static int ventoy_env_init(void) { char buf[64]; grub_env_set("vtdebug_flag", ""); grub_env_export("vtdebug_flag"); g_tree_script_buf = grub_malloc(VTOY_MAX_SCRIPT_BUF); g_list_script_buf = grub_malloc(VTOY_MAX_SCRIPT_BUF); ventoy_filt_register(0, ventoy_wrapper_open); g_grub_param = (ventoy_grub_param *)grub_zalloc(sizeof(ventoy_grub_param)); if (g_grub_param) { g_grub_param->grub_env_get = grub_env_get; grub_snprintf(buf, sizeof(buf), "%p", g_grub_param); grub_env_set("env_param", buf); } return 0; } static cmd_para ventoy_cmds[] = { { "vt_incr", ventoy_cmd_incr, 0, NULL, "{Var} {INT}", "Increase integer variable", NULL }, { "vt_debug", ventoy_cmd_debug, 0, NULL, "{on|off}", "turn debug on/off", NULL }, { "vtdebug", ventoy_cmd_debug, 0, NULL, "{on|off}", "turn debug on/off", NULL }, { "vtbreak", ventoy_cmd_break, 0, NULL, "{level}", "set debug break", NULL }, { "vt_cmp", ventoy_cmd_cmp, 0, NULL, "{Int1} { eq|ne|gt|lt|ge|le } {Int2}", "Comare two integers", NULL }, { "vt_device", ventoy_cmd_device, 0, NULL, "path var", "", NULL }, { "vt_check_compatible", ventoy_cmd_check_compatible, 0, NULL, "", "", NULL }, { "vt_list_img", ventoy_cmd_list_img, 0, NULL, "{device} {cntvar}", "find all iso file in device", NULL }, { "vt_clear_img", ventoy_cmd_clear_img, 0, NULL, "", "clear image list", NULL }, { "vt_img_name", ventoy_cmd_img_name, 0, NULL, "{imageID} {var}", "get image name", NULL }, { "vt_chosen_img_path", ventoy_cmd_chosen_img_path, 0, NULL, "{var}", "get chosen img path", NULL }, { "vt_img_sector", ventoy_cmd_img_sector, 0, NULL, "{imageName}", "", NULL }, { "vt_dump_img_sector", ventoy_cmd_dump_img_sector, 0, NULL, "", "", NULL }, { "vt_load_cpio", ventoy_cmd_load_cpio, 0, NULL, "", "", NULL }, { "vt_find_first_bootable_hd", ventoy_cmd_find_bootable_hdd, 0, NULL, "", "", NULL }, { "vt_dump_menu", ventoy_cmd_dump_menu, 0, NULL, "", "", NULL }, { "vt_dynamic_menu", ventoy_cmd_dynamic_menu, 0, NULL, "", "", NULL }, { "vt_check_mode", ventoy_cmd_check_mode, 0, NULL, "", "", NULL }, { "vt_dump_auto_install", ventoy_cmd_dump_auto_install, 0, NULL, "", "", NULL }, { "vt_is_udf", ventoy_cmd_is_udf, 0, NULL, "", "", NULL }, { "vt_file_size", ventoy_cmd_file_size, 0, NULL, "", "", NULL }, { "vt_load_iso_to_mem", ventoy_cmd_load_iso_to_mem, 0, NULL, "", "", NULL }, { "vt_linux_parse_initrd_isolinux", ventoy_cmd_isolinux_initrd_collect, 0, NULL, "{cfgfile}", "", NULL }, { "vt_linux_parse_initrd_grub", ventoy_cmd_grub_initrd_collect, 0, NULL, "{cfgfile}", "", NULL }, { "vt_linux_specify_initrd_file", ventoy_cmd_specify_initrd_file, 0, NULL, "", "", NULL }, { "vt_linux_clear_initrd", ventoy_cmd_clear_initrd_list, 0, NULL, "", "", NULL }, { "vt_linux_dump_initrd", ventoy_cmd_dump_initrd_list, 0, NULL, "", "", NULL }, { "vt_linux_initrd_count", ventoy_cmd_initrd_count, 0, NULL, "", "", NULL }, { "vt_linux_valid_initrd_count", ventoy_cmd_valid_initrd_count, 0, NULL, "", "", NULL }, { "vt_linux_locate_initrd", ventoy_cmd_linux_locate_initrd, 0, NULL, "", "", NULL }, { "vt_linux_chain_data", ventoy_cmd_linux_chain_data, 0, NULL, "", "", NULL }, { "vt_windows_reset", ventoy_cmd_wimdows_reset, 0, NULL, "", "", NULL }, { "vt_windows_locate_wim", ventoy_cmd_wimdows_locate_wim, 0, NULL, "", "", NULL }, { "vt_windows_chain_data", ventoy_cmd_windows_chain_data, 0, NULL, "", "", NULL }, { "vt_add_replace_file", ventoy_cmd_add_replace_file, 0, NULL, "", "", NULL }, { "vt_relocator_chaindata", ventoy_cmd_relocator_chaindata, 0, NULL, "", "", NULL }, { "vt_test_block_list", ventoy_cmd_test_block_list, 0, NULL, "", "", NULL }, { "vt_load_plugin", ventoy_cmd_load_plugin, 0, NULL, "", "", NULL }, }; GRUB_MOD_INIT(ventoy) { grub_uint32_t i; cmd_para *cur = NULL; ventoy_env_init(); for (i = 0; i < ARRAY_SIZE(ventoy_cmds); i++) { cur = ventoy_cmds + i; cur->cmd = grub_register_extcmd(cur->name, cur->func, cur->flags, cur->summary, cur->description, cur->parser); } } GRUB_MOD_FINI(ventoy) { grub_uint32_t i; for (i = 0; i < ARRAY_SIZE(ventoy_cmds); i++) { grub_unregister_extcmd(ventoy_cmds[i].cmd); } }