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