/****************************************************************************** * ventoy_windows.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 #include #include #include #include "ventoy_def.h" GRUB_MOD_LICENSE ("GPLv3+"); static int g_iso_fs_type = 0; static int g_wim_total_patch_count = 0; static int g_wim_valid_patch_count = 0; static wim_patch *g_wim_patch_head = NULL; static grub_uint64_t g_suppress_wincd_override_offset = 0; static grub_uint32_t g_suppress_wincd_override_data = 0; grub_uint8_t g_temp_buf[512]; grub_ssize_t lzx_decompress ( const void *data, grub_size_t len, void *buf ); grub_ssize_t xca_decompress ( const void *data, grub_size_t len, void *buf ); static wim_patch *ventoy_find_wim_patch(const char *path) { int len = (int)grub_strlen(path); wim_patch *node = g_wim_patch_head; while (node) { if (len == node->pathlen && 0 == grub_strcmp(path, node->path)) { return node; } node = node->next; } return NULL; } static int ventoy_collect_wim_patch(const char *bcdfile) { int i, j, k; int rc = 1; grub_uint64_t magic; grub_file_t file = NULL; char *buf = NULL; wim_patch *node = NULL; char c; grub_uint8_t byte; char valid; char path[256]; g_ventoy_case_insensitive = 1; file = grub_file_open(bcdfile, VENTOY_FILE_TYPE); g_ventoy_case_insensitive = 0; if (!file) { debug("Failed to open file %s\n", bcdfile); grub_errno = 0; goto end; } buf = grub_malloc(file->size + 8); if (!buf) { goto end; } grub_file_read(file, buf, file->size); for (i = 0; i < (int)file->size - 8; i++) { if (buf[i + 8] != 0) { continue; } magic = *(grub_uint64_t *)(buf + i); /* .wim .WIM .Wim */ if ((magic == 0x006D00690077002EULL) || (magic == 0x004D00490057002EULL) || (magic == 0x006D00690057002EULL)) { for (j = i; j > 0; j-= 2) { if (*(grub_uint16_t *)(buf + j) == 0) { break; } } if (j > 0) { byte = (grub_uint8_t)(*(grub_uint16_t *)(buf + j + 2)); if (byte != '/' && byte != '\\') { continue; } valid = 1; for (k = 0, j += 2; k < (int)sizeof(path) - 1 && j < i + 8; j += 2) { byte = (grub_uint8_t)(*(grub_uint16_t *)(buf + j)); c = (char)byte; if (byte > '~' || byte < ' ') /* not printable */ { valid = 0; break; } else if (c == '\\') { c = '/'; } path[k++] = c; } path[k++] = 0; debug("@@@@ Find wim flag:<%s>\n", path); if (0 == valid) { debug("Invalid wim file %d\n", k); } else if (NULL == ventoy_find_wim_patch(path)) { node = grub_zalloc(sizeof(wim_patch)); if (node) { node->pathlen = grub_snprintf(node->path, sizeof(node->path), "%s", path); debug("add patch <%s>\n", path); if (g_wim_patch_head) { node->next = g_wim_patch_head; } g_wim_patch_head = node; g_wim_total_patch_count++; } } else { debug("wim <%s> already exist\n", path); } } } } end: check_free(file, grub_file_close); grub_check_free(buf); return rc; } grub_err_t ventoy_cmd_wim_patch_count(grub_extcmd_context_t ctxt, int argc, char **args) { char buf[32]; (void)ctxt; (void)argc; (void)args; if (argc == 1) { grub_snprintf(buf, sizeof(buf), "%d", g_wim_total_patch_count); ventoy_set_env(args[0], buf); } return 0; } grub_err_t ventoy_cmd_collect_wim_patch(grub_extcmd_context_t ctxt, int argc, char **args) { wim_patch *node = NULL; (void)ctxt; (void)argc; (void)args; if (argc != 2) { return 1; } debug("ventoy_cmd_collect_wim_patch %s %s\n", args[0], args[1]); if (grub_strcmp(args[0], "bcd") == 0) { ventoy_collect_wim_patch(args[1]); return 0; } if (NULL == ventoy_find_wim_patch(args[1])) { node = grub_zalloc(sizeof(wim_patch)); if (node) { node->pathlen = grub_snprintf(node->path, sizeof(node->path), "%s", args[1]); debug("add patch <%s>\n", args[1]); if (g_wim_patch_head) { node->next = g_wim_patch_head; } g_wim_patch_head = node; g_wim_total_patch_count++; } } return 0; } grub_err_t ventoy_cmd_dump_wim_patch(grub_extcmd_context_t ctxt, int argc, char **args) { int i = 0; wim_patch *node = NULL; (void)ctxt; (void)argc; (void)args; for (node = g_wim_patch_head; node; node = node->next) { grub_printf("%d %s [%s]\n", i++, node->path, node->valid ? "SUCCESS" : "FAIL"); } return 0; } static int wim_name_cmp(const char *search, grub_uint16_t *name, grub_uint16_t namelen) { char c1 = vtoy_to_upper(*search); char c2 = vtoy_to_upper(*name); while (namelen > 0 && (c1 == c2)) { search++; name++; namelen--; c1 = vtoy_to_upper(*search); c2 = vtoy_to_upper(*name); } if (namelen == 0 && *search == 0) { return 0; } return 1; } static int ventoy_is_pe64(grub_uint8_t *buffer) { grub_uint32_t pe_off; if (buffer[0] != 'M' || buffer[1] != 'Z') { return 0; } pe_off = *(grub_uint32_t *)(buffer + 60); if (buffer[pe_off] != 'P' || buffer[pe_off + 1] != 'E') { return 0; } if (*(grub_uint16_t *)(buffer + pe_off + 24) == 0x020b) { return 1; } return 0; } grub_err_t ventoy_cmd_is_pe64(grub_extcmd_context_t ctxt, int argc, char **args) { int ret = 1; grub_file_t file; grub_uint8_t buf[512]; (void)ctxt; (void)argc; file = grub_file_open(args[0], VENTOY_FILE_TYPE); if (!file) { return 1; } grub_memset(buf, 0, 512); grub_file_read(file, buf, 512); if (ventoy_is_pe64(buf)) { debug("%s is PE64\n", args[0]); ret = 0; } else { debug("%s is PE32\n", args[0]); } grub_file_close(file); return ret; } grub_err_t ventoy_cmd_sel_wimboot(grub_extcmd_context_t ctxt, int argc, char **args) { int size; char *buf = NULL; char configfile[128]; (void)ctxt; (void)argc; (void)args; debug("select wimboot argc:%d\n", argc); buf = (char *)grub_malloc(8192); if (!buf) { return 0; } size = (int)grub_snprintf(buf, 8192, "menuentry \"Windows Setup (32-bit)\" {\n" " set vtoy_wimboot_sel=32\n" "}\n" "menuentry \"Windows Setup (64-bit)\" {\n" " set vtoy_wimboot_sel=64\n" "}\n" ); buf[size] = 0; g_ventoy_menu_esc = 1; g_ventoy_suppress_esc = 1; grub_snprintf(configfile, sizeof(configfile), "configfile mem:0x%llx:size:%d", (ulonglong)(ulong)buf, size); grub_script_execute_sourcecode(configfile); g_ventoy_menu_esc = 0; g_ventoy_suppress_esc = 0; grub_free(buf); if (g_ventoy_last_entry == 0) { debug("last entry=%d %s=32\n", g_ventoy_last_entry, args[0]); grub_env_set(args[0], "32"); } else { debug("last entry=%d %s=64\n", g_ventoy_last_entry, args[0]); grub_env_set(args[0], "64"); } VENTOY_CMD_RETURN(GRUB_ERR_NONE); } grub_err_t ventoy_cmd_wimdows_reset(grub_extcmd_context_t ctxt, int argc, char **args) { wim_patch *next = NULL; wim_patch *node = g_wim_patch_head; (void)ctxt; (void)argc; (void)args; while (node) { next = node->next; grub_free(node); node = next; } g_wim_patch_head = NULL; g_wim_total_patch_count = 0; g_wim_valid_patch_count = 0; return 0; } static int ventoy_load_jump_exe(const char *path, grub_uint8_t **data, grub_uint32_t *size, wim_hash *hash) { grub_uint32_t i; grub_uint32_t align; grub_file_t file; debug("windows load jump %s\n", path); file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", path); if (!file) { debug("Can't open file %s\n", path); return 1; } align = ventoy_align((int)file->size, 2048); debug("file %s size:%d align:%u\n", path, (int)file->size, align); *size = (grub_uint32_t)file->size; *data = (grub_uint8_t *)grub_malloc(align); if ((*data) == NULL) { debug("Failed to alloc memory size %u\n", align); goto end; } grub_file_read(file, (*data), file->size); if (hash) { grub_crypto_hash(GRUB_MD_SHA1, hash->sha1, (*data), file->size); if (g_ventoy_debug) { debug("%s", "jump bin 64 hash: "); for (i = 0; i < sizeof(hash->sha1); i++) { ventoy_debug("%02x ", hash->sha1[i]); } ventoy_debug("\n"); } } end: grub_file_close(file); return 0; } static int ventoy_get_override_info(grub_file_t file, wim_tail *wim_data) { grub_uint32_t start_block; grub_uint64_t file_offset; grub_uint64_t override_offset; grub_uint32_t override_len; grub_uint64_t fe_entry_size_offset; if (grub_strcmp(file->fs->name, "iso9660") == 0) { g_iso_fs_type = wim_data->iso_type = 0; override_len = sizeof(ventoy_iso9660_override); override_offset = grub_iso9660_get_last_file_dirent_pos(file) + 2; grub_file_read(file, &start_block, 1); // just read for hook trigger file_offset = grub_iso9660_get_last_read_pos(file); debug("iso9660 wim size:%llu override_offset:%llu file_offset:%llu\n", (ulonglong)file->size, (ulonglong)override_offset, (ulonglong)file_offset); } else { g_iso_fs_type = wim_data->iso_type = 1; override_len = sizeof(ventoy_udf_override); override_offset = grub_udf_get_last_file_attr_offset(file, &start_block, &fe_entry_size_offset); file_offset = grub_udf_get_file_offset(file); debug("UDF wim size:%llu override_offset:%llu file_offset:%llu start_block=%u\n", (ulonglong)file->size, (ulonglong)override_offset, (ulonglong)file_offset, start_block); } wim_data->file_offset = file_offset; wim_data->udf_start_block = start_block; wim_data->fe_entry_size_offset = fe_entry_size_offset; wim_data->override_offset = override_offset; wim_data->override_len = override_len; return 0; } static int ventoy_read_resource(grub_file_t fp, wim_header *wimhdr, wim_resource_header *head, void **buffer) { int decompress_len = 0; int total_decompress = 0; grub_uint32_t i = 0; grub_uint32_t chunk_num = 0; grub_uint32_t chunk_size = 0; grub_uint32_t last_chunk_size = 0; grub_uint32_t last_decompress_size = 0; grub_uint32_t cur_offset = 0; grub_uint8_t *cur_dst = NULL; grub_uint8_t *buffer_compress = NULL; grub_uint8_t *buffer_decompress = NULL; grub_uint32_t *chunk_offset = NULL; buffer_decompress = (grub_uint8_t *)grub_malloc(head->raw_size + head->size_in_wim); if (NULL == buffer_decompress) { return 0; } grub_file_seek(fp, head->offset); if (head->size_in_wim == head->raw_size) { grub_file_read(fp, buffer_decompress, head->size_in_wim); *buffer = buffer_decompress; return 0; } buffer_compress = buffer_decompress + head->raw_size; grub_file_read(fp, buffer_compress, head->size_in_wim); chunk_num = (head->raw_size + WIM_CHUNK_LEN - 1) / WIM_CHUNK_LEN; cur_offset = (chunk_num - 1) * 4; chunk_offset = (grub_uint32_t *)buffer_compress; //debug("%llu %llu chunk_num=%lu", (ulonglong)head->size_in_wim, (ulonglong)head->raw_size, chunk_num); cur_dst = buffer_decompress; for (i = 0; i < chunk_num - 1; i++) { chunk_size = (i == 0) ? chunk_offset[i] : chunk_offset[i] - chunk_offset[i - 1]; if (WIM_CHUNK_LEN == chunk_size) { grub_memcpy(cur_dst, buffer_compress + cur_offset, chunk_size); decompress_len = (int)chunk_size; } else { if (wimhdr->flags & FLAG_HEADER_COMPRESS_XPRESS) { decompress_len = (int)xca_decompress(buffer_compress + cur_offset, chunk_size, cur_dst); } else { decompress_len = (int)lzx_decompress(buffer_compress + cur_offset, chunk_size, cur_dst); } } //debug("chunk_size:%u decompresslen:%d\n", chunk_size, decompress_len); total_decompress += decompress_len; cur_dst += decompress_len; cur_offset += chunk_size; } /* last chunk */ last_chunk_size = (grub_uint32_t)(head->size_in_wim - cur_offset); last_decompress_size = head->raw_size - total_decompress; if (last_chunk_size < WIM_CHUNK_LEN && last_chunk_size == last_decompress_size) { debug("Last chunk %u uncompressed\n", last_chunk_size); grub_memcpy(cur_dst, buffer_compress + cur_offset, last_chunk_size); decompress_len = (int)last_chunk_size; } else { if (wimhdr->flags & FLAG_HEADER_COMPRESS_XPRESS) { decompress_len = (int)xca_decompress(buffer_compress + cur_offset, head->size_in_wim - cur_offset, cur_dst); } else { decompress_len = (int)lzx_decompress(buffer_compress + cur_offset, head->size_in_wim - cur_offset, cur_dst); } } cur_dst += decompress_len; total_decompress += decompress_len; //debug("last chunk_size:%u decompresslen:%d tot:%d\n", last_chunk_size, decompress_len, total_decompress); if (cur_dst != buffer_decompress + head->raw_size) { debug("head->size_in_wim:%llu head->raw_size:%llu cur_dst:%p buffer_decompress:%p total_decompress:%d\n", (ulonglong)head->size_in_wim, (ulonglong)head->raw_size, cur_dst, buffer_decompress, total_decompress); grub_free(buffer_decompress); return 1; } *buffer = buffer_decompress; return 0; } static wim_directory_entry * search_wim_dirent(wim_directory_entry *dir, const char *search_name) { do { if (dir->len && dir->name_len) { if (wim_name_cmp(search_name, (grub_uint16_t *)(dir + 1), dir->name_len / 2) == 0) { return dir; } } dir = (wim_directory_entry *)((grub_uint8_t *)dir + dir->len); } while(dir->len); return NULL; } static wim_directory_entry * search_full_wim_dirent ( void *meta_data, wim_directory_entry *dir, const char **path ) { wim_directory_entry *subdir = NULL; wim_directory_entry *search = dir; while (*path) { subdir = (wim_directory_entry *)((char *)meta_data + search->subdir); search = search_wim_dirent(subdir, *path); path++; } return search; } static wim_lookup_entry * ventoy_find_look_entry(wim_header *header, wim_lookup_entry *lookup, wim_hash *hash) { grub_uint32_t i = 0; for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++) { if (grub_memcmp(&lookup[i].hash, hash, sizeof(wim_hash)) == 0) { return lookup + i; } } return NULL; } static int parse_registry_setup_cmdline ( grub_file_t file, wim_header *head, wim_lookup_entry *lookup, void *meta_data, wim_directory_entry *dir, char *buf, grub_uint32_t buflen ) { char c; int ret = 0; grub_uint32_t i = 0; grub_uint32_t reglen = 0; wim_hash zerohash; reg_vk *regvk = NULL; wim_lookup_entry *look = NULL; wim_directory_entry *wim_dirent = NULL; char *decompress_data = NULL; const char *reg_path[] = { "Windows", "System32", "config", "SYSTEM", NULL }; wim_dirent = search_full_wim_dirent(meta_data, dir, reg_path); debug("search reg SYSTEM %p\n", wim_dirent); if (!wim_dirent) { return 1; } grub_memset(&zerohash, 0, sizeof(zerohash)); if (grub_memcmp(&zerohash, wim_dirent->hash.sha1, sizeof(wim_hash)) == 0) { return 2; } look = ventoy_find_look_entry(head, lookup, &wim_dirent->hash); if (!look) { return 3; } reglen = (grub_uint32_t)look->resource.raw_size; debug("find system lookup entry_id:%ld raw_size:%u\n", ((long)look - (long)lookup) / sizeof(wim_lookup_entry), reglen); if (0 != ventoy_read_resource(file, head, &(look->resource), (void **)&(decompress_data))) { return 4; } if (grub_strncmp(decompress_data + 0x1000, "hbin", 4)) { ret_goto_end(5); } for (i = 0x1000; i + sizeof(reg_vk) < reglen; i += 8) { regvk = (reg_vk *)(decompress_data + i); if (regvk->sig == 0x6B76 && regvk->namesize == 7 && regvk->datatype == 1 && regvk->flag == 1) { if (grub_strncasecmp((char *)(regvk + 1), "cmdline", 7) == 0) { debug("find registry cmdline i:%u offset:(0x%x)%u size:(0x%x)%u\n", i, regvk->dataoffset, regvk->dataoffset, regvk->datasize, regvk->datasize); break; } } } if (i + sizeof(reg_vk) >= reglen || regvk == NULL) { ret_goto_end(6); } if (regvk->datasize == 0 || (regvk->datasize & 0x80000000) > 0 || regvk->dataoffset == 0 || regvk->dataoffset == 0xFFFFFFFF) { ret_goto_end(7); } if (regvk->datasize / 2 >= buflen) { ret_goto_end(8); } debug("start offset is 0x%x(%u)\n", 0x1000 + regvk->dataoffset + 4, 0x1000 + regvk->dataoffset + 4); for (i = 0; i < regvk->datasize; i+=2) { c = (char)(*(grub_uint16_t *)(decompress_data + 0x1000 + regvk->dataoffset + 4 + i)); *buf++ = c; } ret = 0; end: grub_check_free(decompress_data); return ret; } static wim_directory_entry * search_replace_wim_dirent ( grub_file_t file, wim_header *head, wim_lookup_entry *lookup, void *meta_data, wim_directory_entry *dir ) { int ret; char cmdline[256] = {0}; wim_directory_entry *wim_dirent = NULL; wim_directory_entry *pecmd_dirent = NULL; const char *peset_path[] = { "Windows", "System32", "peset.exe", NULL }; const char *pecmd_path[] = { "Windows", "System32", "pecmd.exe", NULL }; const char *winpeshl_path[] = { "Windows", "System32", "winpeshl.exe", NULL }; pecmd_dirent = search_full_wim_dirent(meta_data, dir, pecmd_path); debug("search pecmd.exe %p\n", pecmd_dirent); if (pecmd_dirent) { ret = parse_registry_setup_cmdline(file, head, lookup, meta_data, dir, cmdline, sizeof(cmdline)); if (0 == ret) { debug("registry setup cmdline:<%s>\n", cmdline); ventoy_str_toupper(cmdline); if (grub_strncmp(cmdline, "PECMD", 5) == 0) { wim_dirent = pecmd_dirent; } else if (grub_strncmp(cmdline, "PESET", 5) == 0) { wim_dirent = search_full_wim_dirent(meta_data, dir, peset_path); debug("search peset.exe %p\n", wim_dirent); } else if (grub_strncmp(cmdline, "WINPESHL", 8) == 0) { wim_dirent = search_full_wim_dirent(meta_data, dir, winpeshl_path); debug("search winpeshl.exe %p\n", wim_dirent); } if (wim_dirent) { return wim_dirent; } } else { debug("registry setup cmdline failed : %d\n", ret); } } wim_dirent = pecmd_dirent; if (wim_dirent) { return wim_dirent; } wim_dirent = search_full_wim_dirent(meta_data, dir, winpeshl_path); debug("search winpeshl.exe %p\n", wim_dirent); if (wim_dirent) { return wim_dirent; } return NULL; } static wim_lookup_entry * ventoy_find_meta_entry(wim_header *header, wim_lookup_entry *lookup) { grub_uint32_t i = 0; grub_uint32_t index = 0;; if ((header == NULL) || (lookup == NULL)) { return NULL; } for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++) { if (lookup[i].resource.flags & RESHDR_FLAG_METADATA) { index++; if (index == header->boot_index) { return lookup + i; } } } return NULL; } static grub_uint64_t ventoy_get_stream_len(wim_directory_entry *dir) { grub_uint16_t i; grub_uint64_t offset = 0; wim_stream_entry *stream = (wim_stream_entry *)((char *)dir + dir->len); for (i = 0; i < dir->streams; i++) { offset += stream->len; stream = (wim_stream_entry *)((char *)stream + stream->len); } return offset; } static int ventoy_update_stream_hash(wim_patch *patch, wim_directory_entry *dir) { grub_uint16_t i; grub_uint64_t offset = 0; wim_stream_entry *stream = (wim_stream_entry *)((char *)dir + dir->len); for (i = 0; i < dir->streams; i++) { if (grub_memcmp(stream->hash.sha1, patch->old_hash.sha1, sizeof(wim_hash)) == 0) { debug("find target stream %u, name_len:%u upadte hash\n", i, stream->name_len); grub_memcpy(stream->hash.sha1, &(patch->wim_data.bin_hash), sizeof(wim_hash)); } offset += stream->len; stream = (wim_stream_entry *)((char *)stream + stream->len); } return offset; } static int ventoy_update_all_hash(wim_patch *patch, void *meta_data, wim_directory_entry *dir) { if ((meta_data == NULL) || (dir == NULL)) { return 0; } if (dir->len < sizeof(wim_directory_entry)) { return 0; } do { if (dir->subdir == 0 && grub_memcmp(dir->hash.sha1, patch->old_hash.sha1, sizeof(wim_hash)) == 0) { debug("find target file, name_len:%u upadte hash\n", dir->name_len); grub_memcpy(dir->hash.sha1, &(patch->wim_data.bin_hash), sizeof(wim_hash)); } if (dir->subdir) { ventoy_update_all_hash(patch, meta_data, (wim_directory_entry *)((char *)meta_data + dir->subdir)); } if (dir->streams) { ventoy_update_stream_hash(patch, dir); dir = (wim_directory_entry *)((char *)dir + dir->len + ventoy_get_stream_len(dir)); } else { dir = (wim_directory_entry *)((char *)dir + dir->len); } } while (dir->len >= sizeof(wim_directory_entry)); return 0; } static int ventoy_cat_exe_file_data(wim_tail *wim_data, grub_uint32_t exe_len, grub_uint8_t *exe_data) { int pe64 = 0; char file[256]; grub_uint32_t jump_len = 0; grub_uint32_t jump_align = 0; grub_uint8_t *jump_data = NULL; pe64 = ventoy_is_pe64(exe_data); grub_snprintf(file, sizeof(file), "%s/vtoyjump%d.exe", grub_env_get("vtoy_path"), pe64 ? 64 : 32); ventoy_load_jump_exe(file, &jump_data, &jump_len, NULL); jump_align = ventoy_align(jump_len, 16); wim_data->jump_exe_len = jump_len; wim_data->bin_raw_len = jump_align + sizeof(ventoy_os_param) + sizeof(ventoy_windows_data) + exe_len; wim_data->bin_align_len = ventoy_align(wim_data->bin_raw_len, 2048); wim_data->jump_bin_data = grub_malloc(wim_data->bin_align_len); if (wim_data->jump_bin_data) { grub_memcpy(wim_data->jump_bin_data, jump_data, jump_len); grub_memcpy(wim_data->jump_bin_data + jump_align + sizeof(ventoy_os_param) + sizeof(ventoy_windows_data), exe_data, exe_len); } debug("jump_exe_len:%u bin_raw_len:%u bin_align_len:%u\n", wim_data->jump_exe_len, wim_data->bin_raw_len, wim_data->bin_align_len); return 0; } int ventoy_fill_windows_rtdata(void *buf, char *isopath) { char *pos = NULL; char *script = NULL; ventoy_windows_data *data = (ventoy_windows_data *)buf; grub_memset(data, 0, sizeof(ventoy_windows_data)); pos = grub_strstr(isopath, "/"); if (!pos) { return 1; } script = ventoy_plugin_get_cur_install_template(pos); if (script) { debug("auto install script <%s>\n", script); grub_snprintf(data->auto_install_script, sizeof(data->auto_install_script) - 1, "%s", script); } else { debug("auto install script skipped or not configed %s\n", pos); } script = (char *)ventoy_plugin_get_injection(pos); if (script) { if (ventoy_check_file_exist("%s%s", ventoy_get_env("vtoy_iso_part"), script)) { debug("injection archive <%s> OK\n", script); grub_snprintf(data->injection_archive, sizeof(data->injection_archive) - 1, "%s", script); } else { debug("injection archive <%s> NOT exist\n", script); } } else { debug("injection archive not configed %s\n", pos); } return 0; } static int ventoy_update_before_chain(ventoy_os_param *param, char *isopath) { grub_uint32_t jump_align = 0; wim_lookup_entry *meta_look = NULL; wim_security_header *security = NULL; wim_directory_entry *rootdir = NULL; wim_header *head = NULL; wim_lookup_entry *lookup = NULL; wim_patch *node = NULL; wim_tail *wim_data = NULL; for (node = g_wim_patch_head; node; node = node->next) { if (0 == node->valid) { continue; } wim_data = &node->wim_data; head = &wim_data->wim_header; lookup = (wim_lookup_entry *)wim_data->new_lookup_data; jump_align = ventoy_align(wim_data->jump_exe_len, 16); if (wim_data->jump_bin_data) { grub_memcpy(wim_data->jump_bin_data + jump_align, param, sizeof(ventoy_os_param)); ventoy_fill_windows_rtdata(wim_data->jump_bin_data + jump_align + sizeof(ventoy_os_param), isopath); } grub_crypto_hash(GRUB_MD_SHA1, wim_data->bin_hash.sha1, wim_data->jump_bin_data, wim_data->bin_raw_len); security = (wim_security_header *)wim_data->new_meta_data; if (security->len > 0) { rootdir = (wim_directory_entry *)(wim_data->new_meta_data + ((security->len + 7) & 0xFFFFFFF8U)); } else { rootdir = (wim_directory_entry *)(wim_data->new_meta_data + 8); } /* update all winpeshl.exe dirent entry's hash */ ventoy_update_all_hash(node, wim_data->new_meta_data, rootdir); /* update winpeshl.exe lookup entry data (hash/offset/length) */ if (node->replace_look) { debug("update replace lookup entry_id:%ld\n", ((long)node->replace_look - (long)lookup) / sizeof(wim_lookup_entry)); node->replace_look->resource.raw_size = wim_data->bin_raw_len; node->replace_look->resource.size_in_wim = wim_data->bin_raw_len; node->replace_look->resource.flags = 0; node->replace_look->resource.offset = wim_data->wim_align_size; grub_memcpy(node->replace_look->hash.sha1, wim_data->bin_hash.sha1, sizeof(wim_hash)); } /* update metadata's hash */ meta_look = ventoy_find_meta_entry(head, lookup); if (meta_look) { debug("find meta lookup entry_id:%ld\n", ((long)meta_look - (long)lookup) / sizeof(wim_lookup_entry)); grub_memcpy(&meta_look->resource, &head->metadata, sizeof(wim_resource_header)); grub_crypto_hash(GRUB_MD_SHA1, meta_look->hash.sha1, wim_data->new_meta_data, wim_data->new_meta_len); } } return 0; } static int ventoy_wimdows_locate_wim(const char *disk, wim_patch *patch) { int rc; grub_uint16_t i; grub_file_t file; grub_uint32_t exe_len; grub_uint8_t *exe_data = NULL; grub_uint8_t *decompress_data = NULL; wim_lookup_entry *lookup = NULL; wim_security_header *security = NULL; wim_directory_entry *rootdir = NULL; wim_directory_entry *search = NULL; wim_stream_entry *stream = NULL; wim_header *head = &(patch->wim_data.wim_header); wim_tail *wim_data = &patch->wim_data; debug("windows locate wim start %s\n", patch->path); g_ventoy_case_insensitive = 1; file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", disk, patch->path); g_ventoy_case_insensitive = 0; if (!file) { debug("File %s%s NOT exist\n", disk, patch->path); return 1; } ventoy_get_override_info(file, &patch->wim_data); grub_file_seek(file, 0); grub_file_read(file, head, sizeof(wim_header)); if (grub_memcmp(head->signature, WIM_HEAD_SIGNATURE, sizeof(head->signature))) { debug("Not a valid wim file %s\n", (char *)head->signature); grub_file_close(file); return 1; } if (head->flags & FLAG_HEADER_COMPRESS_LZMS) { debug("LZMS compress is not supported 0x%x\n", head->flags); grub_file_close(file); return 1; } rc = ventoy_read_resource(file, head, &head->metadata, (void **)&decompress_data); if (rc) { grub_printf("failed to read meta data %d\n", rc); grub_file_close(file); return 1; } security = (wim_security_header *)decompress_data; if (security->len > 0) { rootdir = (wim_directory_entry *)(decompress_data + ((security->len + 7) & 0xFFFFFFF8U)); } else { rootdir = (wim_directory_entry *)(decompress_data + 8); } debug("read lookup offset:%llu size:%llu\n", (ulonglong)head->lookup.offset, (ulonglong)head->lookup.raw_size); lookup = grub_malloc(head->lookup.raw_size); grub_file_seek(file, head->lookup.offset); grub_file_read(file, lookup, head->lookup.raw_size); /* search winpeshl.exe dirent entry */ search = search_replace_wim_dirent(file, head, lookup, decompress_data, rootdir); if (!search) { debug("Failed to find replace file %p\n", search); grub_file_close(file); return 1; } debug("find replace file at %p\n", search); grub_memset(&patch->old_hash, 0, sizeof(wim_hash)); if (grub_memcmp(&patch->old_hash, search->hash.sha1, sizeof(wim_hash)) == 0) { debug("search hash all 0, now do deep search\n"); stream = (wim_stream_entry *)((char *)search + search->len); for (i = 0; i < search->streams; i++) { if (stream->name_len == 0) { grub_memcpy(&patch->old_hash, stream->hash.sha1, sizeof(wim_hash)); debug("new search hash: %02x %02x %02x %02x %02x %02x %02x %02x\n", ventoy_varg_8(patch->old_hash.sha1)); break; } stream = (wim_stream_entry *)((char *)stream + stream->len); } } else { grub_memcpy(&patch->old_hash, search->hash.sha1, sizeof(wim_hash)); } /* find and extact winpeshl.exe */ patch->replace_look = ventoy_find_look_entry(head, lookup, &patch->old_hash); if (patch->replace_look) { exe_len = (grub_uint32_t)patch->replace_look->resource.raw_size; debug("find replace lookup entry_id:%ld raw_size:%u\n", ((long)patch->replace_look - (long)lookup) / sizeof(wim_lookup_entry), exe_len); if (0 == ventoy_read_resource(file, head, &(patch->replace_look->resource), (void **)&(exe_data))) { ventoy_cat_exe_file_data(wim_data, exe_len, exe_data); grub_free(exe_data); } else { debug("failed to read replace file meta data %u\n", exe_len); } } else { debug("failed to find lookup entry for replace file %02x %02x %02x %02x\n", ventoy_varg_4(patch->old_hash.sha1)); } wim_data->wim_raw_size = (grub_uint32_t)file->size; wim_data->wim_align_size = ventoy_align(wim_data->wim_raw_size, 2048); grub_check_free(wim_data->new_meta_data); wim_data->new_meta_data = decompress_data; wim_data->new_meta_len = head->metadata.raw_size; wim_data->new_meta_align_len = ventoy_align(wim_data->new_meta_len, 2048); grub_check_free(wim_data->new_lookup_data); wim_data->new_lookup_data = (grub_uint8_t *)lookup; wim_data->new_lookup_len = (grub_uint32_t)head->lookup.raw_size; wim_data->new_lookup_align_len = ventoy_align(wim_data->new_lookup_len, 2048); head->metadata.flags = RESHDR_FLAG_METADATA; head->metadata.offset = wim_data->wim_align_size + wim_data->bin_align_len; head->metadata.size_in_wim = wim_data->new_meta_len; head->metadata.raw_size = wim_data->new_meta_len; head->lookup.flags = 0; head->lookup.offset = head->metadata.offset + wim_data->new_meta_align_len; head->lookup.size_in_wim = wim_data->new_lookup_len; head->lookup.raw_size = wim_data->new_lookup_len; grub_file_close(file); debug("%s", "windows locate wim finish\n"); return 0; } grub_err_t ventoy_cmd_locate_wim_patch(grub_extcmd_context_t ctxt, int argc, char **args) { wim_patch *node = g_wim_patch_head; (void)ctxt; (void)argc; (void)args; while (node) { if (0 == ventoy_wimdows_locate_wim(args[0], node)) { node->valid = 1; g_wim_valid_patch_count++; } node = node->next; } return 0; } static grub_uint32_t ventoy_get_override_chunk_num(void) { grub_uint32_t chunk_num = 0; if (g_iso_fs_type == 0) { /* ISO9660: */ /* per wim */ /* 1: file_size and file_offset */ /* 2: new wim file header */ chunk_num = g_wim_valid_patch_count * 2; } else { /* UDF: */ /* global: */ /* 1: block count in Partition Descriptor */ /* per wim */ /* 1: file_size in file_entry or extend_file_entry */ /* 2: data_size and position in extend data short ad */ /* 3: new wim file header */ chunk_num = g_wim_valid_patch_count * 3 + 1; } if (g_suppress_wincd_override_offset > 0) { chunk_num++; } return chunk_num; } static void ventoy_fill_suppress_wincd_override_data(void *override) { ventoy_override_chunk *cur = (ventoy_override_chunk *)override; cur->override_size = 4; cur->img_offset = g_suppress_wincd_override_offset; grub_memcpy(cur->override_data, &g_suppress_wincd_override_data, cur->override_size); } static void ventoy_windows_fill_override_data_iso9660( grub_uint64_t isosize, void *override) { grub_uint64_t sector; grub_uint32_t new_wim_size; ventoy_override_chunk *cur; wim_patch *node = NULL; wim_tail *wim_data = NULL; ventoy_iso9660_override *dirent = NULL; sector = (isosize + 2047) / 2048; cur = (ventoy_override_chunk *)override; if (g_suppress_wincd_override_offset > 0) { ventoy_fill_suppress_wincd_override_data(cur); cur++; } debug("ventoy_windows_fill_override_data_iso9660 %lu\n", (ulong)isosize); for (node = g_wim_patch_head; node; node = node->next) { wim_data = &node->wim_data; if (0 == node->valid) { continue; } new_wim_size = wim_data->wim_align_size + wim_data->bin_align_len + wim_data->new_meta_align_len + wim_data->new_lookup_align_len; dirent = (ventoy_iso9660_override *)wim_data->override_data; dirent->first_sector = (grub_uint32_t)sector; dirent->size = new_wim_size; dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector); dirent->size_be = grub_swap_bytes32(dirent->size); sector += (new_wim_size / 2048); /* override 1: position and length in dirent */ cur->img_offset = wim_data->override_offset; cur->override_size = wim_data->override_len; grub_memcpy(cur->override_data, wim_data->override_data, cur->override_size); cur++; /* override 2: new wim file header */ cur->img_offset = wim_data->file_offset; cur->override_size = sizeof(wim_header); grub_memcpy(cur->override_data, &(wim_data->wim_header), cur->override_size); cur++; } return; } static int ventoy_windows_fill_udf_short_ad(grub_file_t isofile, grub_uint32_t curpos, wim_tail *wim_data, grub_uint32_t new_wim_size) { int i; grub_uint32_t total = 0; grub_uint32_t left_size = 0; ventoy_udf_override *udf = NULL; ventoy_udf_override tmp[4]; grub_memset(tmp, 0, sizeof(tmp)); grub_file_seek(isofile, wim_data->override_offset); grub_file_read(isofile, tmp, sizeof(tmp)); left_size = new_wim_size; udf = (ventoy_udf_override *)wim_data->override_data; for (i = 0; i < 4; i++) { total += tmp[i].length; if (total >= wim_data->wim_raw_size) { udf->length = left_size; udf->position = curpos; return 0; } else { udf->length = tmp[i].length; udf->position = curpos; } left_size -= tmp[i].length; curpos += udf->length / 2048; udf++; wim_data->override_len += sizeof(ventoy_udf_override); } debug("######## Too many udf ad ######\n"); return 1; } static void ventoy_windows_fill_override_data_udf(grub_file_t isofile, void *override) { grub_uint32_t data32; grub_uint64_t data64; grub_uint64_t sector; grub_uint32_t new_wim_size; grub_uint64_t total_wim_size = 0; grub_uint32_t udf_start_block = 0; ventoy_override_chunk *cur; wim_patch *node = NULL; wim_tail *wim_data = NULL; sector = (isofile->size + 2047) / 2048; cur = (ventoy_override_chunk *)override; if (g_suppress_wincd_override_offset > 0) { ventoy_fill_suppress_wincd_override_data(cur); cur++; } debug("ventoy_windows_fill_override_data_udf %lu\n", (ulong)isofile->size); for (node = g_wim_patch_head; node; node = node->next) { wim_data = &node->wim_data; if (node->valid) { if (udf_start_block == 0) { udf_start_block = wim_data->udf_start_block; } new_wim_size = wim_data->wim_align_size + wim_data->bin_align_len + wim_data->new_meta_align_len + wim_data->new_lookup_align_len; total_wim_size += new_wim_size; } } //override 1: sector number in pd data cur->img_offset = grub_udf_get_last_pd_size_offset(); cur->override_size = 4; data32 = sector - udf_start_block + (total_wim_size / 2048); grub_memcpy(cur->override_data, &(data32), 4); for (node = g_wim_patch_head; node; node = node->next) { wim_data = &node->wim_data; if (0 == node->valid) { continue; } new_wim_size = wim_data->wim_align_size + wim_data->bin_align_len + wim_data->new_meta_align_len + wim_data->new_lookup_align_len; //override 2: filesize in file_entry cur++; cur->img_offset = wim_data->fe_entry_size_offset; cur->override_size = 8; data64 = new_wim_size; grub_memcpy(cur->override_data, &(data64), 8); /* override 3: position and length in extend data */ ventoy_windows_fill_udf_short_ad(isofile, (grub_uint32_t)sector - udf_start_block, wim_data, new_wim_size); sector += (new_wim_size / 2048); cur++; cur->img_offset = wim_data->override_offset; cur->override_size = wim_data->override_len; grub_memcpy(cur->override_data, wim_data->override_data, cur->override_size); /* override 4: new wim file header */ cur++; cur->img_offset = wim_data->file_offset; cur->override_size = sizeof(wim_header); grub_memcpy(cur->override_data, &(wim_data->wim_header), cur->override_size); } return; } static grub_uint32_t ventoy_windows_get_virt_data_size(void) { grub_uint32_t size = 0; wim_tail *wim_data = NULL; wim_patch *node = g_wim_patch_head; while (node) { if (node->valid) { wim_data = &node->wim_data; size += sizeof(ventoy_virt_chunk) + wim_data->bin_align_len + wim_data->new_meta_align_len + wim_data->new_lookup_align_len; } node = node->next; } return size; } static void ventoy_windows_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain) { grub_uint64_t sector; grub_uint32_t offset; grub_uint32_t wim_secs; grub_uint32_t mem_secs; char *override = NULL; ventoy_virt_chunk *cur = NULL; wim_tail *wim_data = NULL; wim_patch *node = NULL; sector = (isosize + 2047) / 2048; offset = sizeof(ventoy_virt_chunk) * g_wim_valid_patch_count; override = (char *)chain + chain->virt_chunk_offset; cur = (ventoy_virt_chunk *)override; for (node = g_wim_patch_head; node; node = node->next) { if (0 == node->valid) { continue; } wim_data = &node->wim_data; wim_secs = wim_data->wim_align_size / 2048; mem_secs = (wim_data->bin_align_len + wim_data->new_meta_align_len + wim_data->new_lookup_align_len) / 2048; cur->remap_sector_start = sector; cur->remap_sector_end = cur->remap_sector_start + wim_secs; cur->org_sector_start = (grub_uint32_t)(wim_data->file_offset / 2048); cur->mem_sector_start = cur->remap_sector_end; cur->mem_sector_end = cur->mem_sector_start + mem_secs; cur->mem_sector_offset = offset; sector += wim_secs + mem_secs; cur++; grub_memcpy(override + offset, wim_data->jump_bin_data, wim_data->bin_raw_len); offset += wim_data->bin_align_len; grub_memcpy(override + offset, wim_data->new_meta_data, wim_data->new_meta_len); offset += wim_data->new_meta_align_len; grub_memcpy(override + offset, wim_data->new_lookup_data, wim_data->new_lookup_len); offset += wim_data->new_lookup_align_len; chain->virt_img_size_in_bytes += wim_data->wim_align_size + wim_data->bin_align_len + wim_data->new_meta_align_len + wim_data->new_lookup_align_len; } return; } static int ventoy_windows_drive_map(ventoy_chain_head *chain) { grub_disk_t disk; debug("drive map begin <%p> ...\n", chain); if (chain->disk_drive == 0x80) { disk = grub_disk_open("hd1"); if (disk) { grub_disk_close(disk); debug("drive map needed %p\n", disk); chain->drive_map = 0x81; } else { debug("failed to open disk %s\n", "hd1"); } } else { debug("no need to map 0x%x\n", chain->disk_drive); } return 0; } static int ventoy_suppress_windows_cd_prompt(void) { int rc = 1; const char *cdprompt = NULL; grub_uint64_t readpos = 0; grub_file_t file = NULL; grub_uint8_t data[32]; cdprompt = ventoy_get_env("VTOY_WINDOWS_CD_PROMPT"); if (cdprompt && cdprompt[0] == '1' && cdprompt[1] == 0) { debug("VTOY_WINDOWS_CD_PROMPT:<%s>\n", cdprompt); return 0; } g_ventoy_case_insensitive = 1; file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s/boot/bootfix.bin", "(loop)"); g_ventoy_case_insensitive = 0; if (!file) { debug("Failed to open %s\n", "bootfix.bin"); goto end; } grub_file_read(file, data, 32); if (file->fs && file->fs->name && grub_strcmp(file->fs->name, "udf") == 0) { readpos = grub_udf_get_file_offset(file); } else { readpos = grub_iso9660_get_last_read_pos(file); } debug("bootfix.bin readpos:%lu (sector:%lu) data: %02x %02x %02x %02x\n", (ulong)readpos, (ulong)readpos / 2048, data[24], data[25], data[26], data[27]); if (*(grub_uint32_t *)(data + 24) == 0x13cd0080) { g_suppress_wincd_override_offset = readpos + 24; g_suppress_wincd_override_data = 0x13cd00fd; rc = 0; } debug("g_suppress_wincd_override_offset:%lu\n", (ulong)g_suppress_wincd_override_offset); end: check_free(file, grub_file_close); return rc; } grub_err_t ventoy_cmd_windows_wimboot_data(grub_extcmd_context_t ctxt, int argc, char **args) { grub_uint32_t size = 0; const char *addr = NULL; ventoy_chain_head *chain = NULL; ventoy_os_param *param = NULL; char envbuf[64]; (void)ctxt; (void)argc; (void)args; addr = grub_env_get("vtoy_chain_mem_addr"); if (!addr) { debug("Failed to find vtoy_chain_mem_addr\n"); return 1; } chain = (ventoy_chain_head *)(void *)grub_strtoul(addr, NULL, 16); if (grub_memcmp(&g_ventoy_guid, &chain->os_param.guid, 16) != 0) { debug("os_param.guid not match\n"); return 1; } size = sizeof(ventoy_os_param) + sizeof(ventoy_windows_data); param = (ventoy_os_param *)grub_zalloc(size); if (!param) { return 1; } grub_memcpy(param, &chain->os_param, sizeof(ventoy_os_param)); ventoy_fill_windows_rtdata(param + 1, param->vtoy_img_path); grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)param); grub_env_set("vtoy_wimboot_mem_addr", envbuf); debug("vtoy_wimboot_mem_addr: %s\n", envbuf); grub_snprintf(envbuf, sizeof(envbuf), "%u", size); grub_env_set("vtoy_wimboot_mem_size", envbuf); debug("vtoy_wimboot_mem_size: %s\n", envbuf); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } grub_err_t ventoy_cmd_windows_chain_data(grub_extcmd_context_t ctxt, int argc, char **args) { int unknown_image = 0; int ventoy_compatible = 0; grub_uint32_t size = 0; grub_uint64_t isosize = 0; grub_uint32_t boot_catlog = 0; grub_uint32_t img_chunk_size = 0; grub_uint32_t override_size = 0; grub_uint32_t virt_chunk_size = 0; grub_file_t file; grub_disk_t disk; const char *pLastChain = NULL; const char *compatible; ventoy_chain_head *chain; char envbuf[64]; (void)ctxt; (void)argc; debug("chain data begin <%s> ...\n", args[0]); compatible = grub_env_get("ventoy_compatible"); if (compatible && compatible[0] == 'Y') { ventoy_compatible = 1; } if (NULL == g_img_chunk_list.chunk) { grub_printf("ventoy not ready\n"); return 1; } if (0 == ventoy_compatible && g_wim_valid_patch_count == 0) { unknown_image = 1; debug("Warning: %s was not recognized by Ventoy\n", args[0]); } file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); if (!file) { return 1; } isosize = file->size; boot_catlog = ventoy_get_iso_boot_catlog(file); if (boot_catlog) { if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog))) { grub_env_set("LoadIsoEfiDriver", "on"); } } else { if (ventoy_is_efi_os()) { grub_env_set("LoadIsoEfiDriver", "on"); } else { return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]); } } g_suppress_wincd_override_offset = 0; if (!ventoy_is_efi_os()) /* legacy mode */ { ventoy_suppress_windows_cd_prompt(); } img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk); if (ventoy_compatible || unknown_image) { override_size = g_suppress_wincd_override_offset > 0 ? sizeof(ventoy_override_chunk) : 0; size = sizeof(ventoy_chain_head) + img_chunk_size + override_size; } else { override_size = ventoy_get_override_chunk_num() * sizeof(ventoy_override_chunk); virt_chunk_size = ventoy_windows_get_virt_data_size(); size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size; } pLastChain = grub_env_get("vtoy_chain_mem_addr"); if (pLastChain) { chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16); if (chain) { debug("free last chain memory %p\n", chain); grub_free(chain); } } chain = grub_malloc(size); if (!chain) { grub_printf("Failed to alloc chain memory size %u\n", size); grub_file_close(file); return 1; } grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain); grub_env_set("vtoy_chain_mem_addr", envbuf); grub_snprintf(envbuf, sizeof(envbuf), "%u", size); grub_env_set("vtoy_chain_mem_size", envbuf); grub_memset(chain, 0, sizeof(ventoy_chain_head)); /* part 1: os parameter */ g_ventoy_chain_type = ventoy_chain_windows; ventoy_fill_os_param(file, &(chain->os_param)); if (0 == unknown_image) { ventoy_update_before_chain(&(chain->os_param), args[0]); } /* part 2: chain head */ disk = file->device->disk; chain->disk_drive = disk->id; chain->disk_sector_size = (1 << disk->log_sector_size); chain->real_img_size_in_bytes = file->size; chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048; chain->boot_catalog = boot_catlog; if (!ventoy_is_efi_os()) { grub_file_seek(file, boot_catlog * 2048); grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector)); } /* part 3: image chunk */ chain->img_chunk_offset = sizeof(ventoy_chain_head); chain->img_chunk_num = g_img_chunk_list.cur_chunk; grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size); if (ventoy_compatible || unknown_image) { if (g_suppress_wincd_override_offset > 0) { chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size; chain->override_chunk_num = 1; ventoy_fill_suppress_wincd_override_data((char *)chain + chain->override_chunk_offset); } return 0; } if (0 == g_wim_valid_patch_count) { return 0; } /* part 4: override chunk */ chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size; chain->override_chunk_num = ventoy_get_override_chunk_num(); if (g_iso_fs_type == 0) { ventoy_windows_fill_override_data_iso9660(isosize, (char *)chain + chain->override_chunk_offset); } else { ventoy_windows_fill_override_data_udf(file, (char *)chain + chain->override_chunk_offset); } /* part 5: virt chunk */ chain->virt_chunk_offset = chain->override_chunk_offset + override_size; chain->virt_chunk_num = g_wim_valid_patch_count; ventoy_windows_fill_virt_data(isosize, chain); if (ventoy_is_efi_os() == 0) { ventoy_windows_drive_map(chain); } VENTOY_CMD_RETURN(GRUB_ERR_NONE); } static grub_uint32_t ventoy_get_wim_iso_offset(const char *filepath) { grub_uint32_t imgoffset; grub_file_t file; char cmdbuf[128]; grub_snprintf(cmdbuf, sizeof(cmdbuf), "loopback wimiso \"%s\"", filepath); grub_script_execute_sourcecode(cmdbuf); file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", "(wimiso)/boot/boot.wim"); if (!file) { grub_printf("Failed to open boot.wim file in the image file\n"); return 0; } imgoffset = (grub_uint32_t)grub_iso9660_get_last_file_dirent_pos(file) + 2; debug("wimiso wim direct offset: %u\n", imgoffset); grub_file_close(file); grub_script_execute_sourcecode("loopback -d wimiso"); return imgoffset; } static int ventoy_get_wim_chunklist(const char *filename, ventoy_img_chunk_list *wimchunk, grub_uint64_t *wimsize) { grub_file_t wimfile; wimfile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", filename); if (!wimfile) { return 1; } grub_memset(wimchunk, 0, sizeof(ventoy_img_chunk_list)); wimchunk->chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM); if (NULL == wimchunk->chunk) { grub_file_close(wimfile); return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n"); } wimchunk->max_chunk = DEFAULT_CHUNK_NUM; wimchunk->cur_chunk = 0; ventoy_get_block_list(wimfile, wimchunk, wimfile->device->disk->partition->start); *wimsize = wimfile->size; grub_file_close(wimfile); return 0; } grub_err_t ventoy_cmd_wim_check_bootable(grub_extcmd_context_t ctxt, int argc, char **args) { grub_uint32_t boot_index; grub_file_t file = NULL; wim_header *wimhdr = NULL; (void)ctxt; (void)argc; wimhdr = grub_zalloc(sizeof(wim_header)); if (!wimhdr) { return 1; } file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); if (!file) { grub_free(wimhdr); return 1; } grub_file_read(file, wimhdr, sizeof(wim_header)); grub_file_close(file); boot_index = wimhdr->boot_index; grub_free(wimhdr); if (boot_index == 0) { return 1; } VENTOY_CMD_RETURN(GRUB_ERR_NONE); } grub_err_t ventoy_cmd_wim_chain_data(grub_extcmd_context_t ctxt, int argc, char **args) { grub_uint32_t i = 0; grub_uint32_t imgoffset = 0; grub_uint32_t size = 0; grub_uint32_t isosector = 0; grub_uint64_t wimsize = 0; grub_uint32_t boot_catlog = 0; grub_uint32_t img_chunk1_size = 0; grub_uint32_t img_chunk2_size = 0; grub_uint32_t override_size = 0; grub_file_t file; grub_disk_t disk; const char *pLastChain = NULL; ventoy_chain_head *chain; ventoy_iso9660_override *dirent; ventoy_img_chunk *chunknode; ventoy_override_chunk *override; ventoy_img_chunk_list wimchunk; char envbuf[128]; (void)ctxt; (void)argc; debug("wim chain data begin <%s> ...\n", args[0]); if (NULL == g_wimiso_chunk_list.chunk || NULL == g_wimiso_path) { grub_printf("ventoy not ready\n"); return 1; } imgoffset = ventoy_get_wim_iso_offset(g_wimiso_path); if (imgoffset == 0) { grub_printf("image offset not found\n"); return 1; } if (0 != ventoy_get_wim_chunklist(args[0], &wimchunk, &wimsize)) { grub_printf("Failed to get wim chunklist\n"); return 1; } file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", g_wimiso_path); if (!file) { return 1; } boot_catlog = ventoy_get_iso_boot_catlog(file); img_chunk1_size = g_wimiso_chunk_list.cur_chunk * sizeof(ventoy_img_chunk); img_chunk2_size = wimchunk.cur_chunk * sizeof(ventoy_img_chunk); override_size = sizeof(ventoy_override_chunk); size = sizeof(ventoy_chain_head) + img_chunk1_size + img_chunk2_size + override_size; pLastChain = grub_env_get("vtoy_chain_mem_addr"); if (pLastChain) { chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16); if (chain) { debug("free last chain memory %p\n", chain); grub_free(chain); } } chain = grub_malloc(size); if (!chain) { grub_printf("Failed to alloc chain memory size %u\n", size); grub_file_close(file); return 1; } grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain); grub_env_set("vtoy_chain_mem_addr", envbuf); grub_snprintf(envbuf, sizeof(envbuf), "%u", size); grub_env_set("vtoy_chain_mem_size", envbuf); grub_memset(chain, 0, sizeof(ventoy_chain_head)); /* part 1: os parameter */ g_ventoy_chain_type = ventoy_chain_wim; ventoy_fill_os_param(file, &(chain->os_param)); /* part 2: chain head */ disk = file->device->disk; chain->disk_drive = disk->id; chain->disk_sector_size = (1 << disk->log_sector_size); chain->real_img_size_in_bytes = ventoy_align_2k(file->size) + ventoy_align_2k(wimsize); chain->virt_img_size_in_bytes = chain->real_img_size_in_bytes; chain->boot_catalog = boot_catlog; if (!ventoy_is_efi_os()) { grub_file_seek(file, boot_catlog * 2048); grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector)); } /* part 3: image chunk */ chain->img_chunk_offset = sizeof(ventoy_chain_head); chain->img_chunk_num = g_wimiso_chunk_list.cur_chunk + wimchunk.cur_chunk; grub_memcpy((char *)chain + chain->img_chunk_offset, g_wimiso_chunk_list.chunk, img_chunk1_size); /* fs cluster size >= 2048, so don't need to proc align */ /* align by 2048 */ chunknode = wimchunk.chunk + wimchunk.cur_chunk - 1; i = (chunknode->disk_end_sector + 1 - chunknode->disk_start_sector) % 4; if (i) { chunknode->disk_end_sector += 4 - i; } isosector = (grub_uint32_t)((file->size + 2047) / 2048); for (i = 0; i < wimchunk.cur_chunk; i++) { chunknode = wimchunk.chunk + i; chunknode->img_start_sector = isosector; chunknode->img_end_sector = chunknode->img_start_sector + ((chunknode->disk_end_sector + 1 - chunknode->disk_start_sector) / 4) - 1; isosector = chunknode->img_end_sector + 1; } grub_memcpy((char *)chain + chain->img_chunk_offset + img_chunk1_size, wimchunk.chunk, img_chunk2_size); /* part 4: override chunk */ chain->override_chunk_offset = chain->img_chunk_offset + img_chunk1_size + img_chunk2_size; chain->override_chunk_num = 1; override = (ventoy_override_chunk *)((char *)chain + chain->override_chunk_offset); override->img_offset = imgoffset; override->override_size = sizeof(ventoy_iso9660_override); dirent = (ventoy_iso9660_override *)(override->override_data); dirent->first_sector = (grub_uint32_t)((file->size + 2047) / 2048); dirent->size = (grub_uint32_t)(wimsize); dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector); dirent->size_be = grub_swap_bytes32(dirent->size); debug("imgoffset=%u first_sector=0x%x size=0x%x\n", imgoffset, dirent->first_sector, dirent->size); if (ventoy_is_efi_os() == 0) { ventoy_windows_drive_map(chain); } grub_file_close(file); VENTOY_CMD_RETURN(GRUB_ERR_NONE); } int ventoy_chain_file_size(const char *path) { int size; grub_file_t file; file = grub_file_open(path, VENTOY_FILE_TYPE); size = (int)(file->size); grub_file_close(file); return size; } int ventoy_chain_file_read(const char *path, int offset, int len, void *buf) { int size; grub_file_t file; file = grub_file_open(path, VENTOY_FILE_TYPE); grub_file_seek(file, offset); size = grub_file_read(file, buf, len); grub_file_close(file); return size; }