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