]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_windows.c
69f3feab246fe08a71c40157528cea939172e1cb
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / ventoy / ventoy_windows.c
1 /******************************************************************************
2 * ventoy_windows.c
3 *
4 * Copyright (c) 2020, longpanda <admin@ventoy.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include <grub/types.h>
22 #include <grub/misc.h>
23 #include <grub/mm.h>
24 #include <grub/err.h>
25 #include <grub/dl.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>
35 #include <grub/net.h>
36 #include <grub/time.h>
37 #include <grub/crypto.h>
38 #include <grub/ventoy.h>
39 #include "ventoy_def.h"
40
41 GRUB_MOD_LICENSE ("GPLv3+");
42
43 static int g_peset_flag = 0;
44 static int g_iso_fs_type = 0;
45 static int g_wim_total_patch_count = 0;
46 static int g_wim_valid_patch_count = 0;
47 static wim_patch *g_wim_patch_head = NULL;
48
49 static grub_uint64_t g_suppress_wincd_override_offset = 0;
50 static grub_uint32_t g_suppress_wincd_override_data = 0;
51
52 grub_uint8_t g_temp_buf[512];
53
54 grub_ssize_t lzx_decompress ( const void *data, grub_size_t len, void *buf );
55 grub_ssize_t xca_decompress ( const void *data, grub_size_t len, void *buf );
56
57 static wim_patch *ventoy_find_wim_patch(const char *path)
58 {
59 int len = (int)grub_strlen(path);
60 wim_patch *node = g_wim_patch_head;
61
62 while (node)
63 {
64 if (len == node->pathlen && 0 == grub_strcmp(path, node->path))
65 {
66 return node;
67 }
68 node = node->next;
69 }
70
71 return NULL;
72 }
73
74 static int ventoy_collect_wim_patch(const char *bcdfile)
75 {
76 int i, j, k;
77 int rc = 1;
78 grub_uint64_t magic;
79 grub_file_t file = NULL;
80 char *buf = NULL;
81 wim_patch *node = NULL;
82 char c;
83 grub_uint8_t byte;
84 char valid;
85 char path[256];
86
87 g_ventoy_case_insensitive = 1;
88 file = grub_file_open(bcdfile, VENTOY_FILE_TYPE);
89 g_ventoy_case_insensitive = 0;
90 if (!file)
91 {
92 debug("Failed to open file %s\n", bcdfile);
93 grub_errno = 0;
94 goto end;
95 }
96
97 buf = grub_malloc(file->size + 8);
98 if (!buf)
99 {
100 goto end;
101 }
102
103 grub_file_read(file, buf, file->size);
104
105 for (i = 0; i < (int)file->size - 8; i++)
106 {
107 if (buf[i + 8] != 0)
108 {
109 continue;
110 }
111
112 magic = *(grub_uint64_t *)(buf + i);
113
114 /* .wim .WIM .Wim */
115 if ((magic == 0x006D00690077002EULL) ||
116 (magic == 0x004D00490057002EULL) ||
117 (magic == 0x006D00690057002EULL))
118 {
119 for (j = i; j > 0; j-= 2)
120 {
121 if (*(grub_uint16_t *)(buf + j) == 0)
122 {
123 break;
124 }
125 }
126
127 if (j > 0)
128 {
129 byte = (grub_uint8_t)(*(grub_uint16_t *)(buf + j + 2));
130 if (byte != '/' && byte != '\\')
131 {
132 continue;
133 }
134
135 valid = 1;
136 for (k = 0, j += 2; k < (int)sizeof(path) - 1 && j < i + 8; j += 2)
137 {
138 byte = (grub_uint8_t)(*(grub_uint16_t *)(buf + j));
139 c = (char)byte;
140 if (byte > '~' || byte < ' ') /* not printable */
141 {
142 valid = 0;
143 break;
144 }
145 else if (c == '\\')
146 {
147 c = '/';
148 }
149
150 path[k++] = c;
151 }
152 path[k++] = 0;
153
154 debug("@@@@ Find wim flag:<%s>\n", path);
155
156 if (0 == valid)
157 {
158 debug("Invalid wim file %d\n", k);
159 }
160 else if (NULL == ventoy_find_wim_patch(path))
161 {
162 node = grub_zalloc(sizeof(wim_patch));
163 if (node)
164 {
165 node->pathlen = grub_snprintf(node->path, sizeof(node->path), "%s", path);
166
167 debug("add patch <%s>\n", path);
168
169 if (g_wim_patch_head)
170 {
171 node->next = g_wim_patch_head;
172 }
173 g_wim_patch_head = node;
174
175 g_wim_total_patch_count++;
176 }
177 }
178 else
179 {
180 debug("wim <%s> already exist\n", path);
181 }
182 }
183 }
184 }
185
186 end:
187 check_free(file, grub_file_close);
188 grub_check_free(buf);
189 return rc;
190 }
191
192 grub_err_t ventoy_cmd_wim_patch_count(grub_extcmd_context_t ctxt, int argc, char **args)
193 {
194 char buf[32];
195
196 (void)ctxt;
197 (void)argc;
198 (void)args;
199
200 if (argc == 1)
201 {
202 grub_snprintf(buf, sizeof(buf), "%d", g_wim_total_patch_count);
203 ventoy_set_env(args[0], buf);
204 }
205
206 return 0;
207 }
208
209 grub_err_t ventoy_cmd_collect_wim_patch(grub_extcmd_context_t ctxt, int argc, char **args)
210 {
211 wim_patch *node = NULL;
212
213 (void)ctxt;
214 (void)argc;
215 (void)args;
216
217 if (argc != 2)
218 {
219 return 1;
220 }
221
222 debug("ventoy_cmd_collect_wim_patch %s %s\n", args[0], args[1]);
223
224 if (grub_strcmp(args[0], "bcd") == 0)
225 {
226 ventoy_collect_wim_patch(args[1]);
227 return 0;
228 }
229
230 if (NULL == ventoy_find_wim_patch(args[1]))
231 {
232 node = grub_zalloc(sizeof(wim_patch));
233 if (node)
234 {
235 node->pathlen = grub_snprintf(node->path, sizeof(node->path), "%s", args[1]);
236
237 debug("add patch <%s>\n", args[1]);
238
239 if (g_wim_patch_head)
240 {
241 node->next = g_wim_patch_head;
242 }
243 g_wim_patch_head = node;
244
245 g_wim_total_patch_count++;
246 }
247 }
248
249 return 0;
250 }
251
252
253 grub_err_t ventoy_cmd_dump_wim_patch(grub_extcmd_context_t ctxt, int argc, char **args)
254 {
255 int i = 0;
256 wim_patch *node = NULL;
257
258 (void)ctxt;
259 (void)argc;
260 (void)args;
261
262 for (node = g_wim_patch_head; node; node = node->next)
263 {
264 grub_printf("%d %s [%s]\n", i++, node->path, node->valid ? "SUCCESS" : "FAIL");
265 }
266
267 return 0;
268 }
269
270
271 static int wim_name_cmp(const char *search, grub_uint16_t *name, grub_uint16_t namelen)
272 {
273 char c1 = vtoy_to_upper(*search);
274 char c2 = vtoy_to_upper(*name);
275
276 while (namelen > 0 && (c1 == c2))
277 {
278 search++;
279 name++;
280 namelen--;
281
282 c1 = vtoy_to_upper(*search);
283 c2 = vtoy_to_upper(*name);
284 }
285
286 if (namelen == 0 && *search == 0)
287 {
288 return 0;
289 }
290
291 return 1;
292 }
293
294 static int ventoy_is_pe64(grub_uint8_t *buffer)
295 {
296 grub_uint32_t pe_off;
297
298 if (buffer[0] != 'M' || buffer[1] != 'Z')
299 {
300 return 0;
301 }
302
303 pe_off = *(grub_uint32_t *)(buffer + 60);
304
305 if (buffer[pe_off] != 'P' || buffer[pe_off + 1] != 'E')
306 {
307 return 0;
308 }
309
310 if (*(grub_uint16_t *)(buffer + pe_off + 24) == 0x020b)
311 {
312 return 1;
313 }
314
315 return 0;
316 }
317
318 grub_err_t ventoy_cmd_is_pe64(grub_extcmd_context_t ctxt, int argc, char **args)
319 {
320 int ret = 1;
321 grub_file_t file;
322 grub_uint8_t buf[512];
323
324 (void)ctxt;
325 (void)argc;
326
327 file = grub_file_open(args[0], VENTOY_FILE_TYPE);
328 if (!file)
329 {
330 return 1;
331 }
332
333 grub_memset(buf, 0, 512);
334 grub_file_read(file, buf, 512);
335 if (ventoy_is_pe64(buf))
336 {
337 debug("%s is PE64\n", args[0]);
338 ret = 0;
339 }
340 else
341 {
342 debug("%s is PE32\n", args[0]);
343 }
344 grub_file_close(file);
345
346 return ret;
347 }
348
349 grub_err_t ventoy_cmd_sel_wimboot(grub_extcmd_context_t ctxt, int argc, char **args)
350 {
351 int size;
352 char *buf = NULL;
353 char configfile[128];
354
355 (void)ctxt;
356 (void)argc;
357 (void)args;
358
359 debug("select wimboot argc:%d\n", argc);
360
361 buf = (char *)grub_malloc(8192);
362 if (!buf)
363 {
364 return 0;
365 }
366
367 size = (int)grub_snprintf(buf, 8192,
368 "menuentry \"Windows Setup (32-bit)\" {\n"
369 " set vtoy_wimboot_sel=32\n"
370 "}\n"
371 "menuentry \"Windows Setup (64-bit)\" {\n"
372 " set vtoy_wimboot_sel=64\n"
373 "}\n"
374 );
375 buf[size] = 0;
376
377 g_ventoy_menu_esc = 1;
378 g_ventoy_suppress_esc = 1;
379
380 grub_snprintf(configfile, sizeof(configfile), "configfile mem:0x%llx:size:%d", (ulonglong)(ulong)buf, size);
381 grub_script_execute_sourcecode(configfile);
382
383 g_ventoy_menu_esc = 0;
384 g_ventoy_suppress_esc = 0;
385
386 grub_free(buf);
387
388 if (g_ventoy_last_entry == 0)
389 {
390 debug("last entry=%d %s=32\n", g_ventoy_last_entry, args[0]);
391 grub_env_set(args[0], "32");
392 }
393 else
394 {
395 debug("last entry=%d %s=64\n", g_ventoy_last_entry, args[0]);
396 grub_env_set(args[0], "64");
397 }
398
399 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
400 }
401
402 grub_err_t ventoy_cmd_wimdows_reset(grub_extcmd_context_t ctxt, int argc, char **args)
403 {
404 wim_patch *next = NULL;
405 wim_patch *node = g_wim_patch_head;
406
407 (void)ctxt;
408 (void)argc;
409 (void)args;
410
411 while (node)
412 {
413 next = node->next;
414 grub_free(node);
415 node = next;
416 }
417
418 g_wim_patch_head = NULL;
419 g_wim_total_patch_count = 0;
420 g_wim_valid_patch_count = 0;
421 g_peset_flag = 0;
422
423 return 0;
424 }
425
426 static int ventoy_load_jump_exe(const char *path, grub_uint8_t **data, grub_uint32_t *size, wim_hash *hash)
427 {
428 grub_uint32_t i;
429 grub_uint32_t align;
430 grub_file_t file;
431
432 debug("windows load jump %s\n", path);
433
434 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", path);
435 if (!file)
436 {
437 debug("Can't open file %s\n", path);
438 return 1;
439 }
440
441 align = ventoy_align((int)file->size, 2048);
442
443 debug("file %s size:%d align:%u\n", path, (int)file->size, align);
444
445 *size = (grub_uint32_t)file->size;
446 *data = (grub_uint8_t *)grub_malloc(align);
447 if ((*data) == NULL)
448 {
449 debug("Failed to alloc memory size %u\n", align);
450 goto end;
451 }
452
453 grub_file_read(file, (*data), file->size);
454
455 if (hash)
456 {
457 grub_crypto_hash(GRUB_MD_SHA1, hash->sha1, (*data), file->size);
458
459 if (g_ventoy_debug)
460 {
461 debug("%s", "jump bin 64 hash: ");
462 for (i = 0; i < sizeof(hash->sha1); i++)
463 {
464 ventoy_debug("%02x ", hash->sha1[i]);
465 }
466 ventoy_debug("\n");
467 }
468 }
469
470 end:
471
472 grub_file_close(file);
473 return 0;
474 }
475
476 static int ventoy_get_override_info(grub_file_t file, wim_tail *wim_data)
477 {
478 grub_uint32_t start_block;
479 grub_uint64_t file_offset;
480 grub_uint64_t override_offset;
481 grub_uint32_t override_len;
482 grub_uint64_t fe_entry_size_offset;
483
484 if (grub_strcmp(file->fs->name, "iso9660") == 0)
485 {
486 g_iso_fs_type = wim_data->iso_type = 0;
487 override_len = sizeof(ventoy_iso9660_override);
488 override_offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
489
490 grub_file_read(file, &start_block, 1); // just read for hook trigger
491 file_offset = grub_iso9660_get_last_read_pos(file);
492
493 debug("iso9660 wim size:%llu override_offset:%llu file_offset:%llu\n",
494 (ulonglong)file->size, (ulonglong)override_offset, (ulonglong)file_offset);
495 }
496 else
497 {
498 g_iso_fs_type = wim_data->iso_type = 1;
499 override_len = sizeof(ventoy_udf_override);
500 override_offset = grub_udf_get_last_file_attr_offset(file, &start_block, &fe_entry_size_offset);
501
502 file_offset = grub_udf_get_file_offset(file);
503
504 debug("UDF wim size:%llu override_offset:%llu file_offset:%llu start_block=%u\n",
505 (ulonglong)file->size, (ulonglong)override_offset, (ulonglong)file_offset, start_block);
506 }
507
508 wim_data->file_offset = file_offset;
509 wim_data->udf_start_block = start_block;
510 wim_data->fe_entry_size_offset = fe_entry_size_offset;
511 wim_data->override_offset = override_offset;
512 wim_data->override_len = override_len;
513
514 return 0;
515 }
516
517 static int ventoy_read_resource(grub_file_t fp, wim_header *wimhdr, wim_resource_header *head, void **buffer)
518 {
519 int decompress_len = 0;
520 int total_decompress = 0;
521 grub_uint32_t i = 0;
522 grub_uint32_t chunk_num = 0;
523 grub_uint32_t chunk_size = 0;
524 grub_uint32_t last_chunk_size = 0;
525 grub_uint32_t last_decompress_size = 0;
526 grub_uint32_t cur_offset = 0;
527 grub_uint8_t *cur_dst = NULL;
528 grub_uint8_t *buffer_compress = NULL;
529 grub_uint8_t *buffer_decompress = NULL;
530 grub_uint32_t *chunk_offset = NULL;
531
532 buffer_decompress = (grub_uint8_t *)grub_malloc(head->raw_size + head->size_in_wim);
533 if (NULL == buffer_decompress)
534 {
535 return 0;
536 }
537
538 grub_file_seek(fp, head->offset);
539
540 if (head->size_in_wim == head->raw_size)
541 {
542 grub_file_read(fp, buffer_decompress, head->size_in_wim);
543 *buffer = buffer_decompress;
544 return 0;
545 }
546
547 buffer_compress = buffer_decompress + head->raw_size;
548 grub_file_read(fp, buffer_compress, head->size_in_wim);
549
550 chunk_num = (head->raw_size + WIM_CHUNK_LEN - 1) / WIM_CHUNK_LEN;
551 cur_offset = (chunk_num - 1) * 4;
552 chunk_offset = (grub_uint32_t *)buffer_compress;
553
554 //debug("%llu %llu chunk_num=%lu", (ulonglong)head->size_in_wim, (ulonglong)head->raw_size, chunk_num);
555
556 cur_dst = buffer_decompress;
557
558 for (i = 0; i < chunk_num - 1; i++)
559 {
560 chunk_size = (i == 0) ? chunk_offset[i] : chunk_offset[i] - chunk_offset[i - 1];
561
562 if (WIM_CHUNK_LEN == chunk_size)
563 {
564 grub_memcpy(cur_dst, buffer_compress + cur_offset, chunk_size);
565 decompress_len = (int)chunk_size;
566 }
567 else
568 {
569 if (wimhdr->flags & FLAG_HEADER_COMPRESS_XPRESS)
570 {
571 decompress_len = (int)xca_decompress(buffer_compress + cur_offset, chunk_size, cur_dst);
572 }
573 else
574 {
575 decompress_len = (int)lzx_decompress(buffer_compress + cur_offset, chunk_size, cur_dst);
576 }
577 }
578
579 //debug("chunk_size:%u decompresslen:%d\n", chunk_size, decompress_len);
580
581 total_decompress += decompress_len;
582 cur_dst += decompress_len;
583 cur_offset += chunk_size;
584 }
585
586 /* last chunk */
587 last_chunk_size = (grub_uint32_t)(head->size_in_wim - cur_offset);
588 last_decompress_size = head->raw_size - total_decompress;
589
590 if (last_chunk_size < WIM_CHUNK_LEN && last_chunk_size == last_decompress_size)
591 {
592 debug("Last chunk %u uncompressed\n", last_chunk_size);
593 grub_memcpy(cur_dst, buffer_compress + cur_offset, last_chunk_size);
594 decompress_len = (int)last_chunk_size;
595 }
596 else
597 {
598 if (wimhdr->flags & FLAG_HEADER_COMPRESS_XPRESS)
599 {
600 decompress_len = (int)xca_decompress(buffer_compress + cur_offset, head->size_in_wim - cur_offset, cur_dst);
601 }
602 else
603 {
604 decompress_len = (int)lzx_decompress(buffer_compress + cur_offset, head->size_in_wim - cur_offset, cur_dst);
605 }
606 }
607
608 cur_dst += decompress_len;
609 total_decompress += decompress_len;
610
611 //debug("last chunk_size:%u decompresslen:%d tot:%d\n", last_chunk_size, decompress_len, total_decompress);
612
613 if (cur_dst != buffer_decompress + head->raw_size)
614 {
615 debug("head->size_in_wim:%llu head->raw_size:%llu cur_dst:%p buffer_decompress:%p total_decompress:%d\n",
616 (ulonglong)head->size_in_wim, (ulonglong)head->raw_size, cur_dst, buffer_decompress, total_decompress);
617 grub_free(buffer_decompress);
618 return 1;
619 }
620
621 *buffer = buffer_decompress;
622 return 0;
623 }
624
625
626 static wim_directory_entry * search_wim_dirent(wim_directory_entry *dir, const char *search_name)
627 {
628 do
629 {
630 if (dir->len && dir->name_len)
631 {
632 if (wim_name_cmp(search_name, (grub_uint16_t *)(dir + 1), dir->name_len / 2) == 0)
633 {
634 return dir;
635 }
636 }
637 dir = (wim_directory_entry *)((grub_uint8_t *)dir + dir->len);
638 } while(dir->len);
639
640 return NULL;
641 }
642
643 static wim_directory_entry * search_full_wim_dirent
644 (
645 void *meta_data,
646 wim_directory_entry *dir,
647 const char **path
648 )
649 {
650 wim_directory_entry *subdir = NULL;
651 wim_directory_entry *search = dir;
652
653 while (*path)
654 {
655 subdir = (wim_directory_entry *)((char *)meta_data + search->subdir);
656 search = search_wim_dirent(subdir, *path);
657 path++;
658 }
659
660 return search;
661 }
662
663 static wim_directory_entry * search_replace_wim_dirent(void *meta_data, wim_directory_entry *dir)
664 {
665 wim_directory_entry *tmp_dirent = NULL;
666 wim_directory_entry *wim_dirent = NULL;
667 const char *peset_path[] = { "Windows", "System32", "peset.exe", NULL };
668 const char *pecmd_path[] = { "Windows", "System32", "pecmd.exe", NULL };
669 const char *winpeshl_path[] = { "Windows", "System32", "winpeshl.exe", NULL };
670
671 wim_dirent = search_full_wim_dirent(meta_data, dir, pecmd_path);
672 debug("search pecmd.exe %p\n", wim_dirent);
673 if (wim_dirent)
674 {
675 if (g_peset_flag)
676 {
677 tmp_dirent = search_full_wim_dirent(meta_data, dir, peset_path);
678 debug("search peset.exe %p\n", tmp_dirent);
679 if (tmp_dirent)
680 {
681 return tmp_dirent;
682 }
683 }
684
685 return wim_dirent;
686 }
687
688 wim_dirent = search_full_wim_dirent(meta_data, dir, winpeshl_path);
689 debug("search winpeshl.exe %p\n", wim_dirent);
690 if (wim_dirent)
691 {
692 return wim_dirent;
693 }
694
695 return NULL;
696 }
697
698
699 static wim_lookup_entry * ventoy_find_look_entry(wim_header *header, wim_lookup_entry *lookup, wim_hash *hash)
700 {
701 grub_uint32_t i = 0;
702
703 for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++)
704 {
705 if (grub_memcmp(&lookup[i].hash, hash, sizeof(wim_hash)) == 0)
706 {
707 return lookup + i;
708 }
709 }
710
711 return NULL;
712 }
713
714 static wim_lookup_entry * ventoy_find_meta_entry(wim_header *header, wim_lookup_entry *lookup)
715 {
716 grub_uint32_t i = 0;
717 grub_uint32_t index = 0;;
718
719 if ((header == NULL) || (lookup == NULL))
720 {
721 return NULL;
722 }
723
724 for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++)
725 {
726 if (lookup[i].resource.flags & RESHDR_FLAG_METADATA)
727 {
728 index++;
729 if (index == header->boot_index)
730 {
731 return lookup + i;
732 }
733 }
734 }
735
736 return NULL;
737 }
738
739 static grub_uint64_t ventoy_get_stream_len(wim_directory_entry *dir)
740 {
741 grub_uint16_t i;
742 grub_uint64_t offset = 0;
743 wim_stream_entry *stream = (wim_stream_entry *)((char *)dir + dir->len);
744
745 for (i = 0; i < dir->streams; i++)
746 {
747 offset += stream->len;
748 stream = (wim_stream_entry *)((char *)stream + stream->len);
749 }
750
751 return offset;
752 }
753
754 static int ventoy_update_stream_hash(wim_patch *patch, wim_directory_entry *dir)
755 {
756 grub_uint16_t i;
757 grub_uint64_t offset = 0;
758 wim_stream_entry *stream = (wim_stream_entry *)((char *)dir + dir->len);
759
760 for (i = 0; i < dir->streams; i++)
761 {
762 if (grub_memcmp(stream->hash.sha1, patch->old_hash.sha1, sizeof(wim_hash)) == 0)
763 {
764 debug("find target stream %u, name_len:%u upadte hash\n", i, stream->name_len);
765 grub_memcpy(stream->hash.sha1, &(patch->wim_data.bin_hash), sizeof(wim_hash));
766 }
767
768 offset += stream->len;
769 stream = (wim_stream_entry *)((char *)stream + stream->len);
770 }
771
772 return offset;
773 }
774
775 static int ventoy_update_all_hash(wim_patch *patch, void *meta_data, wim_directory_entry *dir)
776 {
777 if ((meta_data == NULL) || (dir == NULL))
778 {
779 return 0;
780 }
781
782 if (dir->len < sizeof(wim_directory_entry))
783 {
784 return 0;
785 }
786
787 do
788 {
789 if (dir->subdir == 0 && grub_memcmp(dir->hash.sha1, patch->old_hash.sha1, sizeof(wim_hash)) == 0)
790 {
791 debug("find target file, name_len:%u upadte hash\n", dir->name_len);
792 grub_memcpy(dir->hash.sha1, &(patch->wim_data.bin_hash), sizeof(wim_hash));
793 }
794
795 if (dir->subdir)
796 {
797 ventoy_update_all_hash(patch, meta_data, (wim_directory_entry *)((char *)meta_data + dir->subdir));
798 }
799
800 if (dir->streams)
801 {
802 ventoy_update_stream_hash(patch, dir);
803 dir = (wim_directory_entry *)((char *)dir + dir->len + ventoy_get_stream_len(dir));
804 }
805 else
806 {
807 dir = (wim_directory_entry *)((char *)dir + dir->len);
808 }
809 } while (dir->len >= sizeof(wim_directory_entry));
810
811 return 0;
812 }
813
814 static int ventoy_cat_exe_file_data(wim_tail *wim_data, grub_uint32_t exe_len, grub_uint8_t *exe_data)
815 {
816 int pe64 = 0;
817 char file[256];
818 grub_uint32_t jump_len = 0;
819 grub_uint32_t jump_align = 0;
820 grub_uint8_t *jump_data = NULL;
821
822 pe64 = ventoy_is_pe64(exe_data);
823
824 grub_snprintf(file, sizeof(file), "%s/vtoyjump%d.exe", grub_env_get("vtoy_path"), pe64 ? 64 : 32);
825 ventoy_load_jump_exe(file, &jump_data, &jump_len, NULL);
826 jump_align = ventoy_align(jump_len, 16);
827
828 wim_data->jump_exe_len = jump_len;
829 wim_data->bin_raw_len = jump_align + sizeof(ventoy_os_param) + sizeof(ventoy_windows_data) + exe_len;
830 wim_data->bin_align_len = ventoy_align(wim_data->bin_raw_len, 2048);
831
832 wim_data->jump_bin_data = grub_malloc(wim_data->bin_align_len);
833 if (wim_data->jump_bin_data)
834 {
835 grub_memcpy(wim_data->jump_bin_data, jump_data, jump_len);
836 grub_memcpy(wim_data->jump_bin_data + jump_align + sizeof(ventoy_os_param) + sizeof(ventoy_windows_data), exe_data, exe_len);
837 }
838
839 debug("jump_exe_len:%u bin_raw_len:%u bin_align_len:%u\n",
840 wim_data->jump_exe_len, wim_data->bin_raw_len, wim_data->bin_align_len);
841
842 return 0;
843 }
844
845 int ventoy_fill_windows_rtdata(void *buf, char *isopath)
846 {
847 char *pos = NULL;
848 char *script = NULL;
849 ventoy_windows_data *data = (ventoy_windows_data *)buf;
850
851 grub_memset(data, 0, sizeof(ventoy_windows_data));
852
853 pos = grub_strstr(isopath, "/");
854 if (!pos)
855 {
856 return 1;
857 }
858
859 script = ventoy_plugin_get_cur_install_template(pos);
860 if (script)
861 {
862 debug("auto install script <%s>\n", script);
863 grub_snprintf(data->auto_install_script, sizeof(data->auto_install_script) - 1, "%s", script);
864 }
865 else
866 {
867 debug("auto install script skipped or not configed %s\n", pos);
868 }
869
870 script = (char *)ventoy_plugin_get_injection(pos);
871 if (script)
872 {
873 if (ventoy_check_file_exist("%s%s", ventoy_get_env("vtoy_iso_part"), script))
874 {
875 debug("injection archive <%s> OK\n", script);
876 grub_snprintf(data->injection_archive, sizeof(data->injection_archive) - 1, "%s", script);
877 }
878 else
879 {
880 debug("injection archive <%s> NOT exist\n", script);
881 }
882 }
883 else
884 {
885 debug("injection archive not configed %s\n", pos);
886 }
887
888 return 0;
889 }
890
891 static int ventoy_update_before_chain(ventoy_os_param *param, char *isopath)
892 {
893 grub_uint32_t jump_align = 0;
894 wim_lookup_entry *meta_look = NULL;
895 wim_security_header *security = NULL;
896 wim_directory_entry *rootdir = NULL;
897 wim_header *head = NULL;
898 wim_lookup_entry *lookup = NULL;
899 wim_patch *node = NULL;
900 wim_tail *wim_data = NULL;
901
902 for (node = g_wim_patch_head; node; node = node->next)
903 {
904 if (0 == node->valid)
905 {
906 continue;
907 }
908
909 wim_data = &node->wim_data;
910 head = &wim_data->wim_header;
911 lookup = (wim_lookup_entry *)wim_data->new_lookup_data;
912
913 jump_align = ventoy_align(wim_data->jump_exe_len, 16);
914 if (wim_data->jump_bin_data)
915 {
916 grub_memcpy(wim_data->jump_bin_data + jump_align, param, sizeof(ventoy_os_param));
917 ventoy_fill_windows_rtdata(wim_data->jump_bin_data + jump_align + sizeof(ventoy_os_param), isopath);
918 }
919
920 grub_crypto_hash(GRUB_MD_SHA1, wim_data->bin_hash.sha1, wim_data->jump_bin_data, wim_data->bin_raw_len);
921
922 security = (wim_security_header *)wim_data->new_meta_data;
923 if (security->len > 0)
924 {
925 rootdir = (wim_directory_entry *)(wim_data->new_meta_data + ((security->len + 7) & 0xFFFFFFF8U));
926 }
927 else
928 {
929 rootdir = (wim_directory_entry *)(wim_data->new_meta_data + 8);
930 }
931
932 /* update all winpeshl.exe dirent entry's hash */
933 ventoy_update_all_hash(node, wim_data->new_meta_data, rootdir);
934
935 /* update winpeshl.exe lookup entry data (hash/offset/length) */
936 if (node->replace_look)
937 {
938 debug("update replace lookup entry_id:%ld\n", ((long)node->replace_look - (long)lookup) / sizeof(wim_lookup_entry));
939 node->replace_look->resource.raw_size = wim_data->bin_raw_len;
940 node->replace_look->resource.size_in_wim = wim_data->bin_raw_len;
941 node->replace_look->resource.flags = 0;
942 node->replace_look->resource.offset = wim_data->wim_align_size;
943
944 grub_memcpy(node->replace_look->hash.sha1, wim_data->bin_hash.sha1, sizeof(wim_hash));
945 }
946
947 /* update metadata's hash */
948 meta_look = ventoy_find_meta_entry(head, lookup);
949 if (meta_look)
950 {
951 debug("find meta lookup entry_id:%ld\n", ((long)meta_look - (long)lookup) / sizeof(wim_lookup_entry));
952 grub_memcpy(&meta_look->resource, &head->metadata, sizeof(wim_resource_header));
953 grub_crypto_hash(GRUB_MD_SHA1, meta_look->hash.sha1, wim_data->new_meta_data, wim_data->new_meta_len);
954 }
955 }
956
957 return 0;
958 }
959
960 static int ventoy_wimdows_locate_wim(const char *disk, wim_patch *patch)
961 {
962 int rc;
963 grub_uint16_t i;
964 grub_file_t file;
965 grub_uint32_t exe_len;
966 grub_uint8_t *exe_data = NULL;
967 grub_uint8_t *decompress_data = NULL;
968 wim_lookup_entry *lookup = NULL;
969 wim_security_header *security = NULL;
970 wim_directory_entry *rootdir = NULL;
971 wim_directory_entry *search = NULL;
972 wim_stream_entry *stream = NULL;
973 wim_header *head = &(patch->wim_data.wim_header);
974 wim_tail *wim_data = &patch->wim_data;
975
976 debug("windows locate wim start %s\n", patch->path);
977
978 g_ventoy_case_insensitive = 1;
979 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", disk, patch->path);
980 g_ventoy_case_insensitive = 0;
981
982 if (!file)
983 {
984 debug("File %s%s NOT exist\n", disk, patch->path);
985 return 1;
986 }
987
988 ventoy_get_override_info(file, &patch->wim_data);
989
990 grub_file_seek(file, 0);
991 grub_file_read(file, head, sizeof(wim_header));
992
993 if (grub_memcmp(head->signature, WIM_HEAD_SIGNATURE, sizeof(head->signature)))
994 {
995 debug("Not a valid wim file %s\n", (char *)head->signature);
996 grub_file_close(file);
997 return 1;
998 }
999
1000 if (head->flags & FLAG_HEADER_COMPRESS_LZMS)
1001 {
1002 debug("LZMS compress is not supported 0x%x\n", head->flags);
1003 grub_file_close(file);
1004 return 1;
1005 }
1006
1007 rc = ventoy_read_resource(file, head, &head->metadata, (void **)&decompress_data);
1008 if (rc)
1009 {
1010 grub_printf("failed to read meta data %d\n", rc);
1011 grub_file_close(file);
1012 return 1;
1013 }
1014
1015 security = (wim_security_header *)decompress_data;
1016 if (security->len > 0)
1017 {
1018 rootdir = (wim_directory_entry *)(decompress_data + ((security->len + 7) & 0xFFFFFFF8U));
1019 }
1020 else
1021 {
1022 rootdir = (wim_directory_entry *)(decompress_data + 8);
1023 }
1024
1025 /* search winpeshl.exe dirent entry */
1026 search = search_replace_wim_dirent(decompress_data, rootdir);
1027 if (!search)
1028 {
1029 debug("Failed to find replace file %p\n", search);
1030 grub_file_close(file);
1031 return 1;
1032 }
1033
1034 debug("find replace file at %p\n", search);
1035
1036 grub_memset(&patch->old_hash, 0, sizeof(wim_hash));
1037 if (grub_memcmp(&patch->old_hash, search->hash.sha1, sizeof(wim_hash)) == 0)
1038 {
1039 debug("search hash all 0, now do deep search\n");
1040 stream = (wim_stream_entry *)((char *)search + search->len);
1041 for (i = 0; i < search->streams; i++)
1042 {
1043 if (stream->name_len == 0)
1044 {
1045 grub_memcpy(&patch->old_hash, stream->hash.sha1, sizeof(wim_hash));
1046 debug("new search hash: %02x %02x %02x %02x %02x %02x %02x %02x\n",
1047 ventoy_varg_8(patch->old_hash.sha1));
1048 break;
1049 }
1050 stream = (wim_stream_entry *)((char *)stream + stream->len);
1051 }
1052 }
1053 else
1054 {
1055 grub_memcpy(&patch->old_hash, search->hash.sha1, sizeof(wim_hash));
1056 }
1057
1058 debug("read lookup offset:%llu size:%llu\n", (ulonglong)head->lookup.offset, (ulonglong)head->lookup.raw_size);
1059 lookup = grub_malloc(head->lookup.raw_size);
1060 grub_file_seek(file, head->lookup.offset);
1061 grub_file_read(file, lookup, head->lookup.raw_size);
1062
1063 /* find and extact winpeshl.exe */
1064 patch->replace_look = ventoy_find_look_entry(head, lookup, &patch->old_hash);
1065 if (patch->replace_look)
1066 {
1067 exe_len = (grub_uint32_t)patch->replace_look->resource.raw_size;
1068 debug("find replace lookup entry_id:%ld raw_size:%u\n",
1069 ((long)patch->replace_look - (long)lookup) / sizeof(wim_lookup_entry), exe_len);
1070
1071 if (0 == ventoy_read_resource(file, head, &(patch->replace_look->resource), (void **)&(exe_data)))
1072 {
1073 ventoy_cat_exe_file_data(wim_data, exe_len, exe_data);
1074 grub_free(exe_data);
1075 }
1076 else
1077 {
1078 debug("failed to read replace file meta data %u\n", exe_len);
1079 }
1080 }
1081 else
1082 {
1083 debug("failed to find lookup entry for replace file %02x %02x %02x %02x\n",
1084 ventoy_varg_4(patch->old_hash.sha1));
1085 }
1086
1087 wim_data->wim_raw_size = (grub_uint32_t)file->size;
1088 wim_data->wim_align_size = ventoy_align(wim_data->wim_raw_size, 2048);
1089
1090 grub_check_free(wim_data->new_meta_data);
1091 wim_data->new_meta_data = decompress_data;
1092 wim_data->new_meta_len = head->metadata.raw_size;
1093 wim_data->new_meta_align_len = ventoy_align(wim_data->new_meta_len, 2048);
1094
1095 grub_check_free(wim_data->new_lookup_data);
1096 wim_data->new_lookup_data = (grub_uint8_t *)lookup;
1097 wim_data->new_lookup_len = (grub_uint32_t)head->lookup.raw_size;
1098 wim_data->new_lookup_align_len = ventoy_align(wim_data->new_lookup_len, 2048);
1099
1100 head->metadata.flags = RESHDR_FLAG_METADATA;
1101 head->metadata.offset = wim_data->wim_align_size + wim_data->bin_align_len;
1102 head->metadata.size_in_wim = wim_data->new_meta_len;
1103 head->metadata.raw_size = wim_data->new_meta_len;
1104
1105 head->lookup.flags = 0;
1106 head->lookup.offset = head->metadata.offset + wim_data->new_meta_align_len;
1107 head->lookup.size_in_wim = wim_data->new_lookup_len;
1108 head->lookup.raw_size = wim_data->new_lookup_len;
1109
1110 grub_file_close(file);
1111
1112 debug("%s", "windows locate wim finish\n");
1113 return 0;
1114 }
1115
1116 grub_err_t ventoy_cmd_locate_wim_patch(grub_extcmd_context_t ctxt, int argc, char **args)
1117 {
1118 wim_patch *node = g_wim_patch_head;
1119
1120 (void)ctxt;
1121 (void)argc;
1122 (void)args;
1123
1124 while (node)
1125 {
1126 if (0 == ventoy_wimdows_locate_wim(args[0], node))
1127 {
1128 node->valid = 1;
1129 g_wim_valid_patch_count++;
1130 }
1131
1132 node = node->next;
1133 }
1134
1135 return 0;
1136 }
1137
1138 static grub_uint32_t ventoy_get_override_chunk_num(void)
1139 {
1140 grub_uint32_t chunk_num = 0;
1141
1142 if (g_iso_fs_type == 0)
1143 {
1144 /* ISO9660: */
1145 /* per wim */
1146 /* 1: file_size and file_offset */
1147 /* 2: new wim file header */
1148 chunk_num = g_wim_valid_patch_count * 2;
1149 }
1150 else
1151 {
1152 /* UDF: */
1153 /* global: */
1154 /* 1: block count in Partition Descriptor */
1155
1156 /* per wim */
1157 /* 1: file_size in file_entry or extend_file_entry */
1158 /* 2: data_size and position in extend data short ad */
1159 /* 3: new wim file header */
1160 chunk_num = g_wim_valid_patch_count * 3 + 1;
1161 }
1162
1163 if (g_suppress_wincd_override_offset > 0)
1164 {
1165 chunk_num++;
1166 }
1167
1168 return chunk_num;
1169 }
1170
1171 static void ventoy_fill_suppress_wincd_override_data(void *override)
1172 {
1173 ventoy_override_chunk *cur = (ventoy_override_chunk *)override;
1174
1175 cur->override_size = 4;
1176 cur->img_offset = g_suppress_wincd_override_offset;
1177 grub_memcpy(cur->override_data, &g_suppress_wincd_override_data, cur->override_size);
1178 }
1179
1180 static void ventoy_windows_fill_override_data_iso9660( grub_uint64_t isosize, void *override)
1181 {
1182 grub_uint64_t sector;
1183 grub_uint32_t new_wim_size;
1184 ventoy_override_chunk *cur;
1185 wim_patch *node = NULL;
1186 wim_tail *wim_data = NULL;
1187 ventoy_iso9660_override *dirent = NULL;
1188
1189 sector = (isosize + 2047) / 2048;
1190
1191 cur = (ventoy_override_chunk *)override;
1192
1193 if (g_suppress_wincd_override_offset > 0)
1194 {
1195 ventoy_fill_suppress_wincd_override_data(cur);
1196 cur++;
1197 }
1198
1199 debug("ventoy_windows_fill_override_data_iso9660 %lu\n", (ulong)isosize);
1200
1201 for (node = g_wim_patch_head; node; node = node->next)
1202 {
1203 wim_data = &node->wim_data;
1204 if (0 == node->valid)
1205 {
1206 continue;
1207 }
1208
1209 new_wim_size = wim_data->wim_align_size + wim_data->bin_align_len +
1210 wim_data->new_meta_align_len + wim_data->new_lookup_align_len;
1211
1212 dirent = (ventoy_iso9660_override *)wim_data->override_data;
1213
1214 dirent->first_sector = (grub_uint32_t)sector;
1215 dirent->size = new_wim_size;
1216 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
1217 dirent->size_be = grub_swap_bytes32(dirent->size);
1218
1219 sector += (new_wim_size / 2048);
1220
1221 /* override 1: position and length in dirent */
1222 cur->img_offset = wim_data->override_offset;
1223 cur->override_size = wim_data->override_len;
1224 grub_memcpy(cur->override_data, wim_data->override_data, cur->override_size);
1225 cur++;
1226
1227 /* override 2: new wim file header */
1228 cur->img_offset = wim_data->file_offset;
1229 cur->override_size = sizeof(wim_header);
1230 grub_memcpy(cur->override_data, &(wim_data->wim_header), cur->override_size);
1231 cur++;
1232 }
1233
1234 return;
1235 }
1236
1237 static int ventoy_windows_fill_udf_short_ad(grub_file_t isofile, grub_uint32_t curpos,
1238 wim_tail *wim_data, grub_uint32_t new_wim_size)
1239 {
1240 int i;
1241 grub_uint32_t total = 0;
1242 grub_uint32_t left_size = 0;
1243 ventoy_udf_override *udf = NULL;
1244 ventoy_udf_override tmp[4];
1245
1246 grub_memset(tmp, 0, sizeof(tmp));
1247 grub_file_seek(isofile, wim_data->override_offset);
1248 grub_file_read(isofile, tmp, sizeof(tmp));
1249
1250 left_size = new_wim_size;
1251 udf = (ventoy_udf_override *)wim_data->override_data;
1252
1253 for (i = 0; i < 4; i++)
1254 {
1255 total += tmp[i].length;
1256 if (total >= wim_data->wim_raw_size)
1257 {
1258 udf->length = left_size;
1259 udf->position = curpos;
1260 return 0;
1261 }
1262 else
1263 {
1264 udf->length = tmp[i].length;
1265 udf->position = curpos;
1266 }
1267
1268 left_size -= tmp[i].length;
1269 curpos += udf->length / 2048;
1270 udf++;
1271 wim_data->override_len += sizeof(ventoy_udf_override);
1272 }
1273
1274 debug("######## Too many udf ad ######\n");
1275 return 1;
1276 }
1277
1278 static void ventoy_windows_fill_override_data_udf(grub_file_t isofile, void *override)
1279 {
1280 grub_uint32_t data32;
1281 grub_uint64_t data64;
1282 grub_uint64_t sector;
1283 grub_uint32_t new_wim_size;
1284 grub_uint64_t total_wim_size = 0;
1285 grub_uint32_t udf_start_block = 0;
1286 ventoy_override_chunk *cur;
1287 wim_patch *node = NULL;
1288 wim_tail *wim_data = NULL;
1289
1290 sector = (isofile->size + 2047) / 2048;
1291
1292 cur = (ventoy_override_chunk *)override;
1293
1294 if (g_suppress_wincd_override_offset > 0)
1295 {
1296 ventoy_fill_suppress_wincd_override_data(cur);
1297 cur++;
1298 }
1299
1300 debug("ventoy_windows_fill_override_data_udf %lu\n", (ulong)isofile->size);
1301
1302 for (node = g_wim_patch_head; node; node = node->next)
1303 {
1304 wim_data = &node->wim_data;
1305 if (node->valid)
1306 {
1307 if (udf_start_block == 0)
1308 {
1309 udf_start_block = wim_data->udf_start_block;
1310 }
1311 new_wim_size = wim_data->wim_align_size + wim_data->bin_align_len +
1312 wim_data->new_meta_align_len + wim_data->new_lookup_align_len;
1313 total_wim_size += new_wim_size;
1314 }
1315 }
1316
1317 //override 1: sector number in pd data
1318 cur->img_offset = grub_udf_get_last_pd_size_offset();
1319 cur->override_size = 4;
1320 data32 = sector - udf_start_block + (total_wim_size / 2048);
1321 grub_memcpy(cur->override_data, &(data32), 4);
1322
1323 for (node = g_wim_patch_head; node; node = node->next)
1324 {
1325 wim_data = &node->wim_data;
1326 if (0 == node->valid)
1327 {
1328 continue;
1329 }
1330
1331 new_wim_size = wim_data->wim_align_size + wim_data->bin_align_len +
1332 wim_data->new_meta_align_len + wim_data->new_lookup_align_len;
1333
1334 //override 2: filesize in file_entry
1335 cur++;
1336 cur->img_offset = wim_data->fe_entry_size_offset;
1337 cur->override_size = 8;
1338 data64 = new_wim_size;
1339 grub_memcpy(cur->override_data, &(data64), 8);
1340
1341 /* override 3: position and length in extend data */
1342 ventoy_windows_fill_udf_short_ad(isofile, (grub_uint32_t)sector - udf_start_block, wim_data, new_wim_size);
1343
1344 sector += (new_wim_size / 2048);
1345
1346 cur++;
1347 cur->img_offset = wim_data->override_offset;
1348 cur->override_size = wim_data->override_len;
1349 grub_memcpy(cur->override_data, wim_data->override_data, cur->override_size);
1350
1351 /* override 4: new wim file header */
1352 cur++;
1353 cur->img_offset = wim_data->file_offset;
1354 cur->override_size = sizeof(wim_header);
1355 grub_memcpy(cur->override_data, &(wim_data->wim_header), cur->override_size);
1356 }
1357
1358 return;
1359 }
1360
1361 static grub_uint32_t ventoy_windows_get_virt_data_size(void)
1362 {
1363 grub_uint32_t size = 0;
1364 wim_tail *wim_data = NULL;
1365 wim_patch *node = g_wim_patch_head;
1366
1367 while (node)
1368 {
1369 if (node->valid)
1370 {
1371 wim_data = &node->wim_data;
1372 size += sizeof(ventoy_virt_chunk) + wim_data->bin_align_len +
1373 wim_data->new_meta_align_len + wim_data->new_lookup_align_len;
1374 }
1375 node = node->next;
1376 }
1377
1378 return size;
1379 }
1380
1381 static void ventoy_windows_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
1382 {
1383 grub_uint64_t sector;
1384 grub_uint32_t offset;
1385 grub_uint32_t wim_secs;
1386 grub_uint32_t mem_secs;
1387 char *override = NULL;
1388 ventoy_virt_chunk *cur = NULL;
1389 wim_tail *wim_data = NULL;
1390 wim_patch *node = NULL;
1391
1392 sector = (isosize + 2047) / 2048;
1393 offset = sizeof(ventoy_virt_chunk) * g_wim_valid_patch_count;
1394
1395 override = (char *)chain + chain->virt_chunk_offset;
1396 cur = (ventoy_virt_chunk *)override;
1397
1398 for (node = g_wim_patch_head; node; node = node->next)
1399 {
1400 if (0 == node->valid)
1401 {
1402 continue;
1403 }
1404
1405 wim_data = &node->wim_data;
1406
1407 wim_secs = wim_data->wim_align_size / 2048;
1408 mem_secs = (wim_data->bin_align_len + wim_data->new_meta_align_len + wim_data->new_lookup_align_len) / 2048;
1409
1410 cur->remap_sector_start = sector;
1411 cur->remap_sector_end = cur->remap_sector_start + wim_secs;
1412 cur->org_sector_start = (grub_uint32_t)(wim_data->file_offset / 2048);
1413
1414 cur->mem_sector_start = cur->remap_sector_end;
1415 cur->mem_sector_end = cur->mem_sector_start + mem_secs;
1416 cur->mem_sector_offset = offset;
1417
1418 sector += wim_secs + mem_secs;
1419 cur++;
1420
1421 grub_memcpy(override + offset, wim_data->jump_bin_data, wim_data->bin_raw_len);
1422 offset += wim_data->bin_align_len;
1423
1424 grub_memcpy(override + offset, wim_data->new_meta_data, wim_data->new_meta_len);
1425 offset += wim_data->new_meta_align_len;
1426
1427 grub_memcpy(override + offset, wim_data->new_lookup_data, wim_data->new_lookup_len);
1428 offset += wim_data->new_lookup_align_len;
1429
1430 chain->virt_img_size_in_bytes += wim_data->wim_align_size +
1431 wim_data->bin_align_len +
1432 wim_data->new_meta_align_len +
1433 wim_data->new_lookup_align_len;
1434 }
1435
1436 return;
1437 }
1438
1439 static int ventoy_windows_drive_map(ventoy_chain_head *chain)
1440 {
1441 grub_disk_t disk;
1442
1443 debug("drive map begin <%p> ...\n", chain);
1444
1445 if (chain->disk_drive == 0x80)
1446 {
1447 disk = grub_disk_open("hd1");
1448 if (disk)
1449 {
1450 grub_disk_close(disk);
1451 debug("drive map needed %p\n", disk);
1452 chain->drive_map = 0x81;
1453 }
1454 else
1455 {
1456 debug("failed to open disk %s\n", "hd1");
1457 }
1458 }
1459 else
1460 {
1461 debug("no need to map 0x%x\n", chain->disk_drive);
1462 }
1463
1464 return 0;
1465 }
1466
1467 static int ventoy_suppress_windows_cd_prompt(void)
1468 {
1469 int rc = 1;
1470 const char *cdprompt = NULL;
1471 grub_uint64_t readpos = 0;
1472 grub_file_t file = NULL;
1473 grub_uint8_t data[32];
1474
1475 cdprompt = ventoy_get_env("VTOY_WINDOWS_CD_PROMPT");
1476 if (cdprompt && cdprompt[0] == '1' && cdprompt[1] == 0)
1477 {
1478 debug("VTOY_WINDOWS_CD_PROMPT:<%s>\n", cdprompt);
1479 return 0;
1480 }
1481
1482 g_ventoy_case_insensitive = 1;
1483 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s/boot/bootfix.bin", "(loop)");
1484 g_ventoy_case_insensitive = 0;
1485
1486 if (!file)
1487 {
1488 debug("Failed to open %s\n", "bootfix.bin");
1489 goto end;
1490 }
1491
1492 grub_file_read(file, data, 32);
1493
1494 if (file->fs && file->fs->name && grub_strcmp(file->fs->name, "udf") == 0)
1495 {
1496 readpos = grub_udf_get_file_offset(file);
1497 }
1498 else
1499 {
1500 readpos = grub_iso9660_get_last_read_pos(file);
1501 }
1502
1503 debug("bootfix.bin readpos:%lu (sector:%lu) data: %02x %02x %02x %02x\n",
1504 (ulong)readpos, (ulong)readpos / 2048, data[24], data[25], data[26], data[27]);
1505
1506 if (*(grub_uint32_t *)(data + 24) == 0x13cd0080)
1507 {
1508 g_suppress_wincd_override_offset = readpos + 24;
1509 g_suppress_wincd_override_data = 0x13cd00fd;
1510
1511 rc = 0;
1512 }
1513
1514 debug("g_suppress_wincd_override_offset:%lu\n", (ulong)g_suppress_wincd_override_offset);
1515
1516 end:
1517 check_free(file, grub_file_close);
1518
1519 return rc;
1520 }
1521
1522 grub_err_t ventoy_cmd_windows_wimboot_data(grub_extcmd_context_t ctxt, int argc, char **args)
1523 {
1524 grub_uint32_t size = 0;
1525 const char *addr = NULL;
1526 ventoy_chain_head *chain = NULL;
1527 ventoy_os_param *param = NULL;
1528 char envbuf[64];
1529
1530 (void)ctxt;
1531 (void)argc;
1532 (void)args;
1533
1534 addr = grub_env_get("vtoy_chain_mem_addr");
1535 if (!addr)
1536 {
1537 debug("Failed to find vtoy_chain_mem_addr\n");
1538 return 1;
1539 }
1540
1541 chain = (ventoy_chain_head *)(void *)grub_strtoul(addr, NULL, 16);
1542
1543 if (grub_memcmp(&g_ventoy_guid, &chain->os_param.guid, 16) != 0)
1544 {
1545 debug("os_param.guid not match\n");
1546 return 1;
1547 }
1548
1549 size = sizeof(ventoy_os_param) + sizeof(ventoy_windows_data);
1550 param = (ventoy_os_param *)grub_zalloc(size);
1551 if (!param)
1552 {
1553 return 1;
1554 }
1555
1556 grub_memcpy(param, &chain->os_param, sizeof(ventoy_os_param));
1557 ventoy_fill_windows_rtdata(param + 1, param->vtoy_img_path);
1558
1559 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)param);
1560 grub_env_set("vtoy_wimboot_mem_addr", envbuf);
1561 debug("vtoy_wimboot_mem_addr: %s\n", envbuf);
1562
1563 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
1564 grub_env_set("vtoy_wimboot_mem_size", envbuf);
1565 debug("vtoy_wimboot_mem_size: %s\n", envbuf);
1566
1567 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1568 }
1569
1570 grub_err_t ventoy_cmd_windows_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
1571 {
1572 int unknown_image = 0;
1573 int ventoy_compatible = 0;
1574 grub_uint32_t size = 0;
1575 grub_uint64_t isosize = 0;
1576 grub_uint32_t boot_catlog = 0;
1577 grub_uint32_t img_chunk_size = 0;
1578 grub_uint32_t override_size = 0;
1579 grub_uint32_t virt_chunk_size = 0;
1580 grub_file_t file;
1581 grub_disk_t disk;
1582 const char *pLastChain = NULL;
1583 const char *compatible;
1584 ventoy_chain_head *chain;
1585 char envbuf[64];
1586
1587 (void)ctxt;
1588 (void)argc;
1589
1590 debug("chain data begin <%s> ...\n", args[0]);
1591
1592 compatible = grub_env_get("ventoy_compatible");
1593 if (compatible && compatible[0] == 'Y')
1594 {
1595 ventoy_compatible = 1;
1596 }
1597
1598 if (NULL == g_img_chunk_list.chunk)
1599 {
1600 grub_printf("ventoy not ready\n");
1601 return 1;
1602 }
1603
1604 if (0 == ventoy_compatible && g_wim_valid_patch_count == 0)
1605 {
1606 unknown_image = 1;
1607 debug("Warning: %s was not recognized by Ventoy\n", args[0]);
1608 }
1609
1610 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
1611 if (!file)
1612 {
1613 return 1;
1614 }
1615
1616 isosize = file->size;
1617
1618 boot_catlog = ventoy_get_iso_boot_catlog(file);
1619 if (boot_catlog)
1620 {
1621 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
1622 {
1623 grub_env_set("LoadIsoEfiDriver", "on");
1624 }
1625 }
1626 else
1627 {
1628 if (ventoy_is_efi_os())
1629 {
1630 grub_env_set("LoadIsoEfiDriver", "on");
1631 }
1632 else
1633 {
1634 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
1635 }
1636 }
1637
1638 g_suppress_wincd_override_offset = 0;
1639 if (!ventoy_is_efi_os()) /* legacy mode */
1640 {
1641 ventoy_suppress_windows_cd_prompt();
1642 }
1643
1644 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1645
1646 if (ventoy_compatible || unknown_image)
1647 {
1648 override_size = g_suppress_wincd_override_offset > 0 ? sizeof(ventoy_override_chunk) : 0;
1649 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size;
1650 }
1651 else
1652 {
1653 override_size = ventoy_get_override_chunk_num() * sizeof(ventoy_override_chunk);
1654 virt_chunk_size = ventoy_windows_get_virt_data_size();
1655 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
1656 }
1657
1658 pLastChain = grub_env_get("vtoy_chain_mem_addr");
1659 if (pLastChain)
1660 {
1661 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
1662 if (chain)
1663 {
1664 debug("free last chain memory %p\n", chain);
1665 grub_free(chain);
1666 }
1667 }
1668
1669 chain = grub_malloc(size);
1670 if (!chain)
1671 {
1672 grub_printf("Failed to alloc chain memory size %u\n", size);
1673 grub_file_close(file);
1674 return 1;
1675 }
1676
1677 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
1678 grub_env_set("vtoy_chain_mem_addr", envbuf);
1679 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
1680 grub_env_set("vtoy_chain_mem_size", envbuf);
1681
1682 grub_memset(chain, 0, sizeof(ventoy_chain_head));
1683
1684 /* part 1: os parameter */
1685 g_ventoy_chain_type = ventoy_chain_windows;
1686 ventoy_fill_os_param(file, &(chain->os_param));
1687
1688 if (0 == unknown_image)
1689 {
1690 ventoy_update_before_chain(&(chain->os_param), args[0]);
1691 }
1692
1693 /* part 2: chain head */
1694 disk = file->device->disk;
1695 chain->disk_drive = disk->id;
1696 chain->disk_sector_size = (1 << disk->log_sector_size);
1697 chain->real_img_size_in_bytes = file->size;
1698 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
1699 chain->boot_catalog = boot_catlog;
1700
1701 if (!ventoy_is_efi_os())
1702 {
1703 grub_file_seek(file, boot_catlog * 2048);
1704 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
1705 }
1706
1707 /* part 3: image chunk */
1708 chain->img_chunk_offset = sizeof(ventoy_chain_head);
1709 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
1710 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
1711
1712 if (ventoy_compatible || unknown_image)
1713 {
1714 if (g_suppress_wincd_override_offset > 0)
1715 {
1716 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
1717 chain->override_chunk_num = 1;
1718 ventoy_fill_suppress_wincd_override_data((char *)chain + chain->override_chunk_offset);
1719 }
1720
1721 return 0;
1722 }
1723
1724 if (0 == g_wim_valid_patch_count)
1725 {
1726 return 0;
1727 }
1728
1729 /* part 4: override chunk */
1730 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
1731 chain->override_chunk_num = ventoy_get_override_chunk_num();
1732
1733 if (g_iso_fs_type == 0)
1734 {
1735 ventoy_windows_fill_override_data_iso9660(isosize, (char *)chain + chain->override_chunk_offset);
1736 }
1737 else
1738 {
1739 ventoy_windows_fill_override_data_udf(file, (char *)chain + chain->override_chunk_offset);
1740 }
1741
1742 /* part 5: virt chunk */
1743 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
1744 chain->virt_chunk_num = g_wim_valid_patch_count;
1745 ventoy_windows_fill_virt_data(isosize, chain);
1746
1747 if (ventoy_is_efi_os() == 0)
1748 {
1749 ventoy_windows_drive_map(chain);
1750 }
1751
1752 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1753 }
1754
1755 static grub_uint32_t ventoy_get_wim_iso_offset(const char *filepath)
1756 {
1757 grub_uint32_t imgoffset;
1758 grub_file_t file;
1759 char cmdbuf[128];
1760
1761 grub_snprintf(cmdbuf, sizeof(cmdbuf), "loopback wimiso \"%s\"", filepath);
1762 grub_script_execute_sourcecode(cmdbuf);
1763
1764 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", "(wimiso)/boot/boot.wim");
1765 if (!file)
1766 {
1767 grub_printf("Failed to open boot.wim file in the image file\n");
1768 return 0;
1769 }
1770
1771 imgoffset = (grub_uint32_t)grub_iso9660_get_last_file_dirent_pos(file) + 2;
1772
1773 debug("wimiso wim direct offset: %u\n", imgoffset);
1774
1775 grub_file_close(file);
1776
1777 grub_script_execute_sourcecode("loopback -d wimiso");
1778
1779 return imgoffset;
1780 }
1781
1782 static int ventoy_get_wim_chunklist(const char *filename, ventoy_img_chunk_list *wimchunk, grub_uint64_t *wimsize)
1783 {
1784 grub_file_t wimfile;
1785
1786 wimfile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", filename);
1787 if (!wimfile)
1788 {
1789 return 1;
1790 }
1791
1792 grub_memset(wimchunk, 0, sizeof(ventoy_img_chunk_list));
1793 wimchunk->chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM);
1794 if (NULL == wimchunk->chunk)
1795 {
1796 grub_file_close(wimfile);
1797 return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n");
1798 }
1799
1800 wimchunk->max_chunk = DEFAULT_CHUNK_NUM;
1801 wimchunk->cur_chunk = 0;
1802
1803 ventoy_get_block_list(wimfile, wimchunk, wimfile->device->disk->partition->start);
1804
1805 *wimsize = wimfile->size;
1806 grub_file_close(wimfile);
1807
1808 return 0;
1809 }
1810
1811 grub_err_t ventoy_cmd_wim_check_bootable(grub_extcmd_context_t ctxt, int argc, char **args)
1812 {
1813 grub_uint32_t boot_index;
1814 grub_file_t file = NULL;
1815 wim_header *wimhdr = NULL;
1816
1817 (void)ctxt;
1818 (void)argc;
1819
1820 wimhdr = grub_zalloc(sizeof(wim_header));
1821 if (!wimhdr)
1822 {
1823 return 1;
1824 }
1825
1826 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
1827 if (!file)
1828 {
1829 grub_free(wimhdr);
1830 return 1;
1831 }
1832
1833 grub_file_read(file, wimhdr, sizeof(wim_header));
1834 grub_file_close(file);
1835 boot_index = wimhdr->boot_index;
1836 grub_free(wimhdr);
1837
1838 if (boot_index == 0)
1839 {
1840 return 1;
1841 }
1842
1843 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1844 }
1845
1846 grub_err_t ventoy_cmd_wim_peset(grub_extcmd_context_t ctxt, int argc, char **args)
1847 {
1848 (void)ctxt;
1849 (void)argc;
1850 (void)args;
1851
1852 g_peset_flag = 1;
1853
1854 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1855 }
1856
1857 grub_err_t ventoy_cmd_wim_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
1858 {
1859 grub_uint32_t i = 0;
1860 grub_uint32_t imgoffset = 0;
1861 grub_uint32_t size = 0;
1862 grub_uint32_t isosector = 0;
1863 grub_uint64_t wimsize = 0;
1864 grub_uint32_t boot_catlog = 0;
1865 grub_uint32_t img_chunk1_size = 0;
1866 grub_uint32_t img_chunk2_size = 0;
1867 grub_uint32_t override_size = 0;
1868 grub_file_t file;
1869 grub_disk_t disk;
1870 const char *pLastChain = NULL;
1871 ventoy_chain_head *chain;
1872 ventoy_iso9660_override *dirent;
1873 ventoy_img_chunk *chunknode;
1874 ventoy_override_chunk *override;
1875 ventoy_img_chunk_list wimchunk;
1876 char envbuf[128];
1877
1878 (void)ctxt;
1879 (void)argc;
1880
1881 debug("wim chain data begin <%s> ...\n", args[0]);
1882
1883 if (NULL == g_wimiso_chunk_list.chunk || NULL == g_wimiso_path)
1884 {
1885 grub_printf("ventoy not ready\n");
1886 return 1;
1887 }
1888
1889 imgoffset = ventoy_get_wim_iso_offset(g_wimiso_path);
1890 if (imgoffset == 0)
1891 {
1892 grub_printf("image offset not found\n");
1893 return 1;
1894 }
1895
1896 if (0 != ventoy_get_wim_chunklist(args[0], &wimchunk, &wimsize))
1897 {
1898 grub_printf("Failed to get wim chunklist\n");
1899 return 1;
1900 }
1901
1902 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", g_wimiso_path);
1903 if (!file)
1904 {
1905 return 1;
1906 }
1907
1908 boot_catlog = ventoy_get_iso_boot_catlog(file);
1909
1910 img_chunk1_size = g_wimiso_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1911 img_chunk2_size = wimchunk.cur_chunk * sizeof(ventoy_img_chunk);
1912 override_size = sizeof(ventoy_override_chunk);
1913
1914 size = sizeof(ventoy_chain_head) + img_chunk1_size + img_chunk2_size + override_size;
1915
1916 pLastChain = grub_env_get("vtoy_chain_mem_addr");
1917 if (pLastChain)
1918 {
1919 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
1920 if (chain)
1921 {
1922 debug("free last chain memory %p\n", chain);
1923 grub_free(chain);
1924 }
1925 }
1926
1927 chain = grub_malloc(size);
1928 if (!chain)
1929 {
1930 grub_printf("Failed to alloc chain memory size %u\n", size);
1931 grub_file_close(file);
1932 return 1;
1933 }
1934
1935 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
1936 grub_env_set("vtoy_chain_mem_addr", envbuf);
1937 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
1938 grub_env_set("vtoy_chain_mem_size", envbuf);
1939
1940 grub_memset(chain, 0, sizeof(ventoy_chain_head));
1941
1942 /* part 1: os parameter */
1943 g_ventoy_chain_type = ventoy_chain_wim;
1944 ventoy_fill_os_param(file, &(chain->os_param));
1945
1946 /* part 2: chain head */
1947 disk = file->device->disk;
1948 chain->disk_drive = disk->id;
1949 chain->disk_sector_size = (1 << disk->log_sector_size);
1950 chain->real_img_size_in_bytes = ventoy_align_2k(file->size) + ventoy_align_2k(wimsize);
1951 chain->virt_img_size_in_bytes = chain->real_img_size_in_bytes;
1952 chain->boot_catalog = boot_catlog;
1953
1954 if (!ventoy_is_efi_os())
1955 {
1956 grub_file_seek(file, boot_catlog * 2048);
1957 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
1958 }
1959
1960 /* part 3: image chunk */
1961 chain->img_chunk_offset = sizeof(ventoy_chain_head);
1962 chain->img_chunk_num = g_wimiso_chunk_list.cur_chunk + wimchunk.cur_chunk;
1963 grub_memcpy((char *)chain + chain->img_chunk_offset, g_wimiso_chunk_list.chunk, img_chunk1_size);
1964
1965 /* fs cluster size >= 2048, so don't need to proc align */
1966
1967 /* align by 2048 */
1968 chunknode = wimchunk.chunk + wimchunk.cur_chunk - 1;
1969 i = (chunknode->disk_end_sector + 1 - chunknode->disk_start_sector) % 4;
1970 if (i)
1971 {
1972 chunknode->disk_end_sector += 4 - i;
1973 }
1974
1975 isosector = (grub_uint32_t)((file->size + 2047) / 2048);
1976 for (i = 0; i < wimchunk.cur_chunk; i++)
1977 {
1978 chunknode = wimchunk.chunk + i;
1979 chunknode->img_start_sector = isosector;
1980 chunknode->img_end_sector = chunknode->img_start_sector +
1981 ((chunknode->disk_end_sector + 1 - chunknode->disk_start_sector) / 4) - 1;
1982 isosector = chunknode->img_end_sector + 1;
1983 }
1984
1985 grub_memcpy((char *)chain + chain->img_chunk_offset + img_chunk1_size, wimchunk.chunk, img_chunk2_size);
1986
1987 /* part 4: override chunk */
1988 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk1_size + img_chunk2_size;
1989 chain->override_chunk_num = 1;
1990
1991 override = (ventoy_override_chunk *)((char *)chain + chain->override_chunk_offset);
1992 override->img_offset = imgoffset;
1993 override->override_size = sizeof(ventoy_iso9660_override);
1994
1995 dirent = (ventoy_iso9660_override *)(override->override_data);
1996 dirent->first_sector = (grub_uint32_t)((file->size + 2047) / 2048);
1997 dirent->size = (grub_uint32_t)(wimsize);
1998 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
1999 dirent->size_be = grub_swap_bytes32(dirent->size);
2000
2001 debug("imgoffset=%u first_sector=0x%x size=0x%x\n", imgoffset, dirent->first_sector, dirent->size);
2002
2003 if (ventoy_is_efi_os() == 0)
2004 {
2005 ventoy_windows_drive_map(chain);
2006 }
2007
2008 grub_file_close(file);
2009
2010 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
2011 }
2012
2013 int ventoy_chain_file_size(const char *path)
2014 {
2015 int size;
2016 grub_file_t file;
2017
2018 file = grub_file_open(path, VENTOY_FILE_TYPE);
2019 size = (int)(file->size);
2020
2021 grub_file_close(file);
2022
2023 return size;
2024 }
2025
2026 int ventoy_chain_file_read(const char *path, int offset, int len, void *buf)
2027 {
2028 int size;
2029 grub_file_t file;
2030
2031 file = grub_file_open(path, VENTOY_FILE_TYPE);
2032 grub_file_seek(file, offset);
2033 size = grub_file_read(file, buf, len);
2034 grub_file_close(file);
2035
2036 return size;
2037 }
2038