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