]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_windows.c
76975dbfa113e7e53da963ec01d1441e65333989
[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 if (!search)
553 {
554 debug("%s search failed\n", *path);
555 }
556
557 path++;
558 }
559 return search;
560 }
561
562 static wim_directory_entry * search_replace_wim_dirent(void *meta_data, wim_directory_entry *dir)
563 {
564 wim_directory_entry *wim_dirent = NULL;
565 const char *winpeshl_path[] = { "Windows", "System32", "winpeshl.exe", NULL };
566 //const char *pecmd_path[] = { "Windows", "System32", "PECMD.exe", NULL };
567
568 wim_dirent = search_full_wim_dirent(meta_data, dir, winpeshl_path);
569 if (wim_dirent)
570 {
571 return wim_dirent;
572 }
573
574 #if 0
575 wim_dirent = search_full_wim_dirent(meta_data, dir, pecmd_path);
576 if (wim_dirent)
577 {
578 return wim_dirent;
579 }
580 #endif
581
582 return NULL;
583 }
584
585
586 static wim_lookup_entry * ventoy_find_look_entry(wim_header *header, wim_lookup_entry *lookup, wim_hash *hash)
587 {
588 grub_uint32_t i = 0;
589
590 for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++)
591 {
592 if (grub_memcmp(&lookup[i].hash, hash, sizeof(wim_hash)) == 0)
593 {
594 return lookup + i;
595 }
596 }
597
598 return NULL;
599 }
600
601 static wim_lookup_entry * ventoy_find_meta_entry(wim_header *header, wim_lookup_entry *lookup)
602 {
603 grub_uint32_t i = 0;
604 grub_uint32_t index = 0;;
605
606 if ((header == NULL) || (lookup == NULL))
607 {
608 return NULL;
609 }
610
611 for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++)
612 {
613 if (lookup[i].resource.flags & RESHDR_FLAG_METADATA)
614 {
615 index++;
616 if (index == header->boot_index)
617 {
618 return lookup + i;
619 }
620 }
621 }
622
623 return NULL;
624 }
625
626 static int ventoy_update_all_hash(wim_patch *patch, void *meta_data, wim_directory_entry *dir)
627 {
628 if ((meta_data == NULL) || (dir == NULL))
629 {
630 return 0;
631 }
632
633 if (dir->len < sizeof(wim_directory_entry))
634 {
635 return 0;
636 }
637
638 do
639 {
640 if (dir->subdir == 0 && grub_memcmp(dir->hash.sha1, patch->old_hash.sha1, sizeof(wim_hash)) == 0)
641 {
642 debug("find target file, name_len:%u upadte hash\n", dir->name_len);
643 grub_memcpy(dir->hash.sha1, &(patch->wim_data.bin_hash), sizeof(wim_hash));
644 }
645
646 if (dir->subdir)
647 {
648 ventoy_update_all_hash(patch, meta_data, (wim_directory_entry *)((char *)meta_data + dir->subdir));
649 }
650
651 dir = (wim_directory_entry *)((char *)dir + dir->len);
652 } while (dir->len >= sizeof(wim_directory_entry));
653
654 return 0;
655 }
656
657 static int ventoy_cat_exe_file_data(wim_tail *wim_data, grub_uint32_t exe_len, grub_uint8_t *exe_data)
658 {
659 int pe64 = 0;
660 char file[256];
661 grub_uint32_t jump_len = 0;
662 grub_uint32_t jump_align = 0;
663 grub_uint8_t *jump_data = NULL;
664
665 pe64 = ventoy_is_pe64(exe_data);
666
667 grub_snprintf(file, sizeof(file), "%s/vtoyjump%d.exe", grub_env_get("vtoy_path"), pe64 ? 64 : 32);
668 ventoy_load_jump_exe(file, &jump_data, &jump_len, NULL);
669 jump_align = ventoy_align(jump_len, 16);
670
671 wim_data->jump_exe_len = jump_len;
672 wim_data->bin_raw_len = jump_align + sizeof(ventoy_os_param) + sizeof(ventoy_windows_data) + exe_len;
673 wim_data->bin_align_len = ventoy_align(wim_data->bin_raw_len, 2048);
674
675 wim_data->jump_bin_data = grub_malloc(wim_data->bin_align_len);
676 if (wim_data->jump_bin_data)
677 {
678 grub_memcpy(wim_data->jump_bin_data, jump_data, jump_len);
679 grub_memcpy(wim_data->jump_bin_data + jump_align + sizeof(ventoy_os_param) + sizeof(ventoy_windows_data), exe_data, exe_len);
680 }
681
682 debug("jump_exe_len:%u bin_raw_len:%u bin_align_len:%u\n",
683 wim_data->jump_exe_len, wim_data->bin_raw_len, wim_data->bin_align_len);
684
685 return 0;
686 }
687
688 int ventoy_fill_windows_rtdata(void *buf, char *isopath)
689 {
690 char *pos = NULL;
691 char *script = NULL;
692 ventoy_windows_data *data = (ventoy_windows_data *)buf;
693
694 grub_memset(data, 0, sizeof(ventoy_windows_data));
695
696 pos = grub_strstr(isopath, "/");
697 if (!pos)
698 {
699 return 1;
700 }
701
702 script = ventoy_plugin_get_cur_install_template(pos);
703 if (script)
704 {
705 debug("auto install script <%s>\n", script);
706 grub_snprintf(data->auto_install_script, sizeof(data->auto_install_script) - 1, "%s", script);
707 }
708 else
709 {
710 debug("auto install script skipped or not configed %s\n", pos);
711 }
712
713 return 0;
714 }
715
716 static int ventoy_update_before_chain(ventoy_os_param *param, char *isopath)
717 {
718 grub_uint32_t jump_align = 0;
719 wim_lookup_entry *meta_look = NULL;
720 wim_security_header *security = NULL;
721 wim_directory_entry *rootdir = NULL;
722 wim_header *head = NULL;
723 wim_lookup_entry *lookup = NULL;
724 wim_patch *node = NULL;
725 wim_tail *wim_data = NULL;
726
727 for (node = g_wim_patch_head; node; node = node->next)
728 {
729 if (0 == node->valid)
730 {
731 continue;
732 }
733
734 wim_data = &node->wim_data;
735 head = &wim_data->wim_header;
736 lookup = (wim_lookup_entry *)wim_data->new_lookup_data;
737
738 jump_align = ventoy_align(wim_data->jump_exe_len, 16);
739 if (wim_data->jump_bin_data)
740 {
741 grub_memcpy(wim_data->jump_bin_data + jump_align, param, sizeof(ventoy_os_param));
742 ventoy_fill_windows_rtdata(wim_data->jump_bin_data + jump_align + sizeof(ventoy_os_param), isopath);
743 }
744
745 grub_crypto_hash(GRUB_MD_SHA1, wim_data->bin_hash.sha1, wim_data->jump_bin_data, wim_data->bin_raw_len);
746
747 security = (wim_security_header *)wim_data->new_meta_data;
748 rootdir = (wim_directory_entry *)(wim_data->new_meta_data + ((security->len + 7) & 0xFFFFFFF8U));
749
750 /* update all winpeshl.exe dirent entry's hash */
751 ventoy_update_all_hash(node, wim_data->new_meta_data, rootdir);
752
753 /* update winpeshl.exe lookup entry data (hash/offset/length) */
754 if (node->replace_look)
755 {
756 debug("update replace lookup entry_id:%ld\n", ((long)node->replace_look - (long)lookup) / sizeof(wim_lookup_entry));
757 node->replace_look->resource.raw_size = wim_data->bin_raw_len;
758 node->replace_look->resource.size_in_wim = wim_data->bin_raw_len;
759 node->replace_look->resource.flags = 0;
760 node->replace_look->resource.offset = wim_data->wim_align_size;
761
762 grub_memcpy(node->replace_look->hash.sha1, wim_data->bin_hash.sha1, sizeof(wim_hash));
763 }
764
765 /* update metadata's hash */
766 meta_look = ventoy_find_meta_entry(head, lookup);
767 if (meta_look)
768 {
769 debug("find meta lookup entry_id:%ld\n", ((long)meta_look - (long)lookup) / sizeof(wim_lookup_entry));
770 grub_memcpy(&meta_look->resource, &head->metadata, sizeof(wim_resource_header));
771 grub_crypto_hash(GRUB_MD_SHA1, meta_look->hash.sha1, wim_data->new_meta_data, wim_data->new_meta_len);
772 }
773 }
774
775 return 0;
776 }
777
778 static int ventoy_wimdows_locate_wim(const char *disk, wim_patch *patch)
779 {
780 int rc;
781 grub_file_t file;
782 grub_uint32_t exe_len;
783 grub_uint8_t *exe_data = NULL;
784 grub_uint8_t *decompress_data = NULL;
785 wim_lookup_entry *lookup = NULL;
786 wim_security_header *security = NULL;
787 wim_directory_entry *rootdir = NULL;
788 wim_directory_entry *search = NULL;
789 wim_header *head = &(patch->wim_data.wim_header);
790 wim_tail *wim_data = &patch->wim_data;
791
792 debug("windows locate wim start %s\n", patch->path);
793
794 g_ventoy_case_insensitive = 1;
795 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", disk, patch->path);
796 g_ventoy_case_insensitive = 0;
797
798 if (!file)
799 {
800 debug("File %s%s NOT exist\n", disk, patch->path);
801 return 1;
802 }
803
804 ventoy_get_override_info(file, &patch->wim_data);
805
806 grub_file_seek(file, 0);
807 grub_file_read(file, head, sizeof(wim_header));
808
809 if (grub_memcmp(head->signature, WIM_HEAD_SIGNATURE, sizeof(head->signature)))
810 {
811 debug("Not a valid wim file %s\n", (char *)head->signature);
812 grub_file_close(file);
813 return 1;
814 }
815
816 if (head->flags & FLAG_HEADER_COMPRESS_XPRESS)
817 {
818 debug("Xpress compress is not supported 0x%x\n", head->flags);
819 grub_file_close(file);
820 return 1;
821 }
822
823 rc = ventoy_read_resource(file, &head->metadata, (void **)&decompress_data);
824 if (rc)
825 {
826 grub_printf("failed to read meta data %d\n", rc);
827 grub_file_close(file);
828 return 1;
829 }
830
831 security = (wim_security_header *)decompress_data;
832 rootdir = (wim_directory_entry *)(decompress_data + ((security->len + 7) & 0xFFFFFFF8U));
833
834 /* search winpeshl.exe dirent entry */
835 search = search_replace_wim_dirent(decompress_data, rootdir);
836 if (!search)
837 {
838 debug("Failed to find replace file %p\n", search);
839 grub_file_close(file);
840 return 1;
841 }
842
843 debug("find replace file at %p\n", search);
844
845 grub_memcpy(&patch->old_hash, search->hash.sha1, sizeof(wim_hash));
846
847 debug("read lookup offset:%llu size:%llu\n", (ulonglong)head->lookup.offset, (ulonglong)head->lookup.raw_size);
848 lookup = grub_malloc(head->lookup.raw_size);
849 grub_file_seek(file, head->lookup.offset);
850 grub_file_read(file, lookup, head->lookup.raw_size);
851
852 /* find and extact winpeshl.exe */
853 patch->replace_look = ventoy_find_look_entry(head, lookup, &patch->old_hash);
854 if (patch->replace_look)
855 {
856 exe_len = (grub_uint32_t)patch->replace_look->resource.raw_size;
857 debug("find replace lookup entry_id:%ld raw_size:%u\n",
858 ((long)patch->replace_look - (long)lookup) / sizeof(wim_lookup_entry), exe_len);
859
860 if (0 == ventoy_read_resource(file, &(patch->replace_look->resource), (void **)&(exe_data)))
861 {
862 ventoy_cat_exe_file_data(wim_data, exe_len, exe_data);
863 grub_free(exe_data);
864 }
865 else
866 {
867 debug("failed to read replace file meta data %u\n", exe_len);
868 }
869 }
870 else
871 {
872 debug("failed to find lookup entry for replace file 0x%02x 0x%02x\n",
873 patch->old_hash.sha1[0], patch->old_hash.sha1[1]);
874 }
875
876 wim_data->wim_raw_size = (grub_uint32_t)file->size;
877 wim_data->wim_align_size = ventoy_align(wim_data->wim_raw_size, 2048);
878
879 grub_check_free(wim_data->new_meta_data);
880 wim_data->new_meta_data = decompress_data;
881 wim_data->new_meta_len = head->metadata.raw_size;
882 wim_data->new_meta_align_len = ventoy_align(wim_data->new_meta_len, 2048);
883
884 grub_check_free(wim_data->new_lookup_data);
885 wim_data->new_lookup_data = (grub_uint8_t *)lookup;
886 wim_data->new_lookup_len = (grub_uint32_t)head->lookup.raw_size;
887 wim_data->new_lookup_align_len = ventoy_align(wim_data->new_lookup_len, 2048);
888
889 head->metadata.flags = RESHDR_FLAG_METADATA;
890 head->metadata.offset = wim_data->wim_align_size + wim_data->bin_align_len;
891 head->metadata.size_in_wim = wim_data->new_meta_len;
892 head->metadata.raw_size = wim_data->new_meta_len;
893
894 head->lookup.flags = 0;
895 head->lookup.offset = head->metadata.offset + wim_data->new_meta_align_len;
896 head->lookup.size_in_wim = wim_data->new_lookup_len;
897 head->lookup.raw_size = wim_data->new_lookup_len;
898
899 grub_file_close(file);
900
901 debug("%s", "windows locate wim finish\n");
902 return 0;
903 }
904
905 grub_err_t ventoy_cmd_locate_wim_patch(grub_extcmd_context_t ctxt, int argc, char **args)
906 {
907 wim_patch *node = g_wim_patch_head;
908
909 (void)ctxt;
910 (void)argc;
911 (void)args;
912
913 while (node)
914 {
915 if (0 == ventoy_wimdows_locate_wim(args[0], node))
916 {
917 node->valid = 1;
918 g_wim_valid_patch_count++;
919 }
920
921 node = node->next;
922 }
923
924 return 0;
925 }
926
927 static grub_uint32_t ventoy_get_override_chunk_num(void)
928 {
929 grub_uint32_t chunk_num = 0;
930
931 if (g_iso_fs_type == 0)
932 {
933 /* ISO9660: */
934 /* per wim */
935 /* 1: file_size and file_offset */
936 /* 2: new wim file header */
937 chunk_num = g_wim_valid_patch_count * 2;
938 }
939 else
940 {
941 /* UDF: */
942 /* global: */
943 /* 1: block count in Partition Descriptor */
944
945 /* per wim */
946 /* 1: file_size in file_entry or extend_file_entry */
947 /* 2: data_size and position in extend data short ad */
948 /* 3: new wim file header */
949 chunk_num = g_wim_valid_patch_count * 3 + 1;
950 }
951
952 if (g_suppress_wincd_override_offset > 0)
953 {
954 chunk_num++;
955 }
956
957 return chunk_num;
958 }
959
960 static void ventoy_fill_suppress_wincd_override_data(void *override)
961 {
962 ventoy_override_chunk *cur = (ventoy_override_chunk *)override;
963
964 cur->override_size = 4;
965 cur->img_offset = g_suppress_wincd_override_offset;
966 grub_memcpy(cur->override_data, &g_suppress_wincd_override_data, cur->override_size);
967 }
968
969 static void ventoy_windows_fill_override_data_iso9660( grub_uint64_t isosize, void *override)
970 {
971 grub_uint64_t sector;
972 grub_uint32_t new_wim_size;
973 ventoy_override_chunk *cur;
974 wim_patch *node = NULL;
975 wim_tail *wim_data = NULL;
976 ventoy_iso9660_override *dirent = NULL;
977
978 sector = (isosize + 2047) / 2048;
979
980 cur = (ventoy_override_chunk *)override;
981
982 if (g_suppress_wincd_override_offset > 0)
983 {
984 ventoy_fill_suppress_wincd_override_data(cur);
985 cur++;
986 }
987
988 debug("ventoy_windows_fill_override_data_iso9660 %lu\n", (ulong)isosize);
989
990 for (node = g_wim_patch_head; node; node = node->next)
991 {
992 wim_data = &node->wim_data;
993 if (0 == node->valid)
994 {
995 continue;
996 }
997
998 new_wim_size = wim_data->wim_align_size + wim_data->bin_align_len +
999 wim_data->new_meta_align_len + wim_data->new_lookup_align_len;
1000
1001 dirent = (ventoy_iso9660_override *)wim_data->override_data;
1002
1003 dirent->first_sector = (grub_uint32_t)sector;
1004 dirent->size = new_wim_size;
1005 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
1006 dirent->size_be = grub_swap_bytes32(dirent->size);
1007
1008 sector += (new_wim_size / 2048);
1009
1010 /* override 1: position and length in dirent */
1011 cur->img_offset = wim_data->override_offset;
1012 cur->override_size = wim_data->override_len;
1013 grub_memcpy(cur->override_data, wim_data->override_data, cur->override_size);
1014 cur++;
1015
1016 /* override 2: new wim file header */
1017 cur->img_offset = wim_data->file_offset;
1018 cur->override_size = sizeof(wim_header);
1019 grub_memcpy(cur->override_data, &(wim_data->wim_header), cur->override_size);
1020 cur++;
1021 }
1022
1023 return;
1024 }
1025
1026 static void ventoy_windows_fill_override_data_udf( grub_uint64_t isosize, void *override)
1027 {
1028 grub_uint32_t data32;
1029 grub_uint64_t data64;
1030 grub_uint64_t sector;
1031 grub_uint32_t new_wim_size;
1032 grub_uint64_t total_wim_size = 0;
1033 grub_uint32_t udf_start_block = 0;
1034 ventoy_override_chunk *cur;
1035 wim_patch *node = NULL;
1036 wim_tail *wim_data = NULL;
1037 ventoy_udf_override *udf = NULL;
1038
1039 sector = (isosize + 2047) / 2048;
1040
1041 cur = (ventoy_override_chunk *)override;
1042
1043 if (g_suppress_wincd_override_offset > 0)
1044 {
1045 ventoy_fill_suppress_wincd_override_data(cur);
1046 cur++;
1047 }
1048
1049 debug("ventoy_windows_fill_override_data_udf %lu\n", (ulong)isosize);
1050
1051 for (node = g_wim_patch_head; node; node = node->next)
1052 {
1053 wim_data = &node->wim_data;
1054 if (node->valid)
1055 {
1056 if (udf_start_block == 0)
1057 {
1058 udf_start_block = wim_data->udf_start_block;
1059 }
1060 new_wim_size = wim_data->wim_align_size + wim_data->bin_align_len +
1061 wim_data->new_meta_align_len + wim_data->new_lookup_align_len;
1062 total_wim_size += new_wim_size;
1063 }
1064 }
1065
1066 //override 1: sector number in pd data
1067 cur->img_offset = grub_udf_get_last_pd_size_offset();
1068 cur->override_size = 4;
1069 data32 = sector - udf_start_block + (total_wim_size / 2048);
1070 grub_memcpy(cur->override_data, &(data32), 4);
1071
1072 for (node = g_wim_patch_head; node; node = node->next)
1073 {
1074 wim_data = &node->wim_data;
1075 if (0 == node->valid)
1076 {
1077 continue;
1078 }
1079
1080 new_wim_size = wim_data->wim_align_size + wim_data->bin_align_len +
1081 wim_data->new_meta_align_len + wim_data->new_lookup_align_len;
1082
1083 //override 2: filesize in file_entry
1084 cur++;
1085 cur->img_offset = wim_data->fe_entry_size_offset;
1086 cur->override_size = 8;
1087 data64 = new_wim_size;
1088 grub_memcpy(cur->override_data, &(data64), 8);
1089
1090 udf = (ventoy_udf_override *)wim_data->override_data;
1091 udf->length = new_wim_size;
1092 udf->position = (grub_uint32_t)sector - udf_start_block;
1093
1094 sector += (new_wim_size / 2048);
1095
1096 /* override 3: position and length in extend data */
1097 cur++;
1098 cur->img_offset = wim_data->override_offset;
1099 cur->override_size = wim_data->override_len;
1100 grub_memcpy(cur->override_data, wim_data->override_data, cur->override_size);
1101
1102 /* override 4: new wim file header */
1103 cur++;
1104 cur->img_offset = wim_data->file_offset;
1105 cur->override_size = sizeof(wim_header);
1106 grub_memcpy(cur->override_data, &(wim_data->wim_header), cur->override_size);
1107 }
1108
1109 return;
1110 }
1111
1112 static grub_uint32_t ventoy_windows_get_virt_data_size(void)
1113 {
1114 grub_uint32_t size = 0;
1115 wim_tail *wim_data = NULL;
1116 wim_patch *node = g_wim_patch_head;
1117
1118 while (node)
1119 {
1120 if (node->valid)
1121 {
1122 wim_data = &node->wim_data;
1123 size += sizeof(ventoy_virt_chunk) + wim_data->bin_align_len +
1124 wim_data->new_meta_align_len + wim_data->new_lookup_align_len;
1125 }
1126 node = node->next;
1127 }
1128
1129 return size;
1130 }
1131
1132 static void ventoy_windows_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
1133 {
1134 grub_uint64_t sector;
1135 grub_uint32_t offset;
1136 grub_uint32_t wim_secs;
1137 grub_uint32_t mem_secs;
1138 char *override = NULL;
1139 ventoy_virt_chunk *cur = NULL;
1140 wim_tail *wim_data = NULL;
1141 wim_patch *node = NULL;
1142
1143 sector = (isosize + 2047) / 2048;
1144 offset = sizeof(ventoy_virt_chunk) * g_wim_valid_patch_count;
1145
1146 override = (char *)chain + chain->virt_chunk_offset;
1147 cur = (ventoy_virt_chunk *)override;
1148
1149 for (node = g_wim_patch_head; node; node = node->next)
1150 {
1151 if (0 == node->valid)
1152 {
1153 continue;
1154 }
1155
1156 wim_data = &node->wim_data;
1157
1158 wim_secs = wim_data->wim_align_size / 2048;
1159 mem_secs = (wim_data->bin_align_len + wim_data->new_meta_align_len + wim_data->new_lookup_align_len) / 2048;
1160
1161 cur->remap_sector_start = sector;
1162 cur->remap_sector_end = cur->remap_sector_start + wim_secs;
1163 cur->org_sector_start = (grub_uint32_t)(wim_data->file_offset / 2048);
1164
1165 cur->mem_sector_start = cur->remap_sector_end;
1166 cur->mem_sector_end = cur->mem_sector_start + mem_secs;
1167 cur->mem_sector_offset = offset;
1168
1169 sector += wim_secs + mem_secs;
1170 cur++;
1171
1172 grub_memcpy(override + offset, wim_data->jump_bin_data, wim_data->bin_raw_len);
1173 offset += wim_data->bin_align_len;
1174
1175 grub_memcpy(override + offset, wim_data->new_meta_data, wim_data->new_meta_len);
1176 offset += wim_data->new_meta_align_len;
1177
1178 grub_memcpy(override + offset, wim_data->new_lookup_data, wim_data->new_lookup_len);
1179 offset += wim_data->new_lookup_align_len;
1180
1181 chain->virt_img_size_in_bytes += wim_data->wim_align_size +
1182 wim_data->bin_align_len +
1183 wim_data->new_meta_align_len +
1184 wim_data->new_lookup_align_len;
1185 }
1186
1187 return;
1188 }
1189
1190 static int ventoy_windows_drive_map(ventoy_chain_head *chain)
1191 {
1192 grub_disk_t disk;
1193
1194 debug("drive map begin <%p> ...\n", chain);
1195
1196 if (chain->disk_drive == 0x80)
1197 {
1198 disk = grub_disk_open("hd1");
1199 if (disk)
1200 {
1201 grub_disk_close(disk);
1202 debug("drive map needed %p\n", disk);
1203 chain->drive_map = 0x81;
1204 }
1205 else
1206 {
1207 debug("failed to open disk %s\n", "hd1");
1208 }
1209 }
1210 else
1211 {
1212 debug("no need to map 0x%x\n", chain->disk_drive);
1213 }
1214
1215 return 0;
1216 }
1217
1218 static int ventoy_suppress_windows_cd_prompt(void)
1219 {
1220 int rc = 1;
1221 const char *cdprompt = NULL;
1222 grub_uint64_t readpos = 0;
1223 grub_file_t file = NULL;
1224 grub_uint8_t data[32];
1225
1226 cdprompt = ventoy_get_env("VTOY_WINDOWS_CD_PROMPT");
1227 if (cdprompt && cdprompt[0] == '1' && cdprompt[1] == 0)
1228 {
1229 debug("VTOY_WINDOWS_CD_PROMPT:<%s>\n", cdprompt);
1230 return 0;
1231 }
1232
1233 g_ventoy_case_insensitive = 1;
1234 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s/boot/bootfix.bin", "(loop)");
1235 g_ventoy_case_insensitive = 0;
1236
1237 if (!file)
1238 {
1239 debug("Failed to open %s\n", "bootfix.bin");
1240 goto end;
1241 }
1242
1243 grub_file_read(file, data, 32);
1244
1245 if (file->fs && file->fs->name && grub_strcmp(file->fs->name, "udf") == 0)
1246 {
1247 readpos = grub_udf_get_file_offset(file);
1248 }
1249 else
1250 {
1251 readpos = grub_iso9660_get_last_read_pos(file);
1252 }
1253
1254 debug("bootfix.bin readpos:%lu (sector:%lu) data: %02x %02x %02x %02x\n",
1255 (ulong)readpos, (ulong)readpos / 2048, data[24], data[25], data[26], data[27]);
1256
1257 if (*(grub_uint32_t *)(data + 24) == 0x13cd0080)
1258 {
1259 g_suppress_wincd_override_offset = readpos + 24;
1260 g_suppress_wincd_override_data = 0x13cd00fd;
1261
1262 rc = 0;
1263 }
1264
1265 debug("g_suppress_wincd_override_offset:%lu\n", (ulong)g_suppress_wincd_override_offset);
1266
1267 end:
1268 check_free(file, grub_file_close);
1269
1270 return rc;
1271 }
1272
1273 grub_err_t ventoy_cmd_windows_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
1274 {
1275 int unknown_image = 0;
1276 int ventoy_compatible = 0;
1277 grub_uint32_t size = 0;
1278 grub_uint64_t isosize = 0;
1279 grub_uint32_t boot_catlog = 0;
1280 grub_uint32_t img_chunk_size = 0;
1281 grub_uint32_t override_size = 0;
1282 grub_uint32_t virt_chunk_size = 0;
1283 grub_file_t file;
1284 grub_disk_t disk;
1285 const char *pLastChain = NULL;
1286 const char *compatible;
1287 ventoy_chain_head *chain;
1288 char envbuf[64];
1289
1290 (void)ctxt;
1291 (void)argc;
1292
1293 debug("chain data begin <%s> ...\n", args[0]);
1294
1295 compatible = grub_env_get("ventoy_compatible");
1296 if (compatible && compatible[0] == 'Y')
1297 {
1298 ventoy_compatible = 1;
1299 }
1300
1301 if (NULL == g_img_chunk_list.chunk)
1302 {
1303 grub_printf("ventoy not ready\n");
1304 return 1;
1305 }
1306
1307 if (0 == ventoy_compatible && g_wim_valid_patch_count == 0)
1308 {
1309 unknown_image = 1;
1310 debug("Warning: %s was not recognized by Ventoy\n", args[0]);
1311 }
1312
1313 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
1314 if (!file)
1315 {
1316 return 1;
1317 }
1318
1319 isosize = file->size;
1320
1321 boot_catlog = ventoy_get_iso_boot_catlog(file);
1322 if (boot_catlog)
1323 {
1324 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
1325 {
1326 grub_env_set("LoadIsoEfiDriver", "on");
1327 }
1328 }
1329 else
1330 {
1331 if (ventoy_is_efi_os())
1332 {
1333 grub_env_set("LoadIsoEfiDriver", "on");
1334 }
1335 else
1336 {
1337 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
1338 }
1339 }
1340
1341 g_suppress_wincd_override_offset = 0;
1342 if (!ventoy_is_efi_os()) /* legacy mode */
1343 {
1344 ventoy_suppress_windows_cd_prompt();
1345 }
1346
1347 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1348
1349 if (ventoy_compatible || unknown_image)
1350 {
1351 override_size = g_suppress_wincd_override_offset > 0 ? sizeof(ventoy_override_chunk) : 0;
1352 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size;
1353 }
1354 else
1355 {
1356 override_size = ventoy_get_override_chunk_num() * sizeof(ventoy_override_chunk);
1357 virt_chunk_size = ventoy_windows_get_virt_data_size();
1358 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
1359 }
1360
1361 pLastChain = grub_env_get("vtoy_chain_mem_addr");
1362 if (pLastChain)
1363 {
1364 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
1365 if (chain)
1366 {
1367 debug("free last chain memory %p\n", chain);
1368 grub_free(chain);
1369 }
1370 }
1371
1372 chain = grub_malloc(size);
1373 if (!chain)
1374 {
1375 grub_printf("Failed to alloc chain memory size %u\n", size);
1376 grub_file_close(file);
1377 return 1;
1378 }
1379
1380 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
1381 grub_env_set("vtoy_chain_mem_addr", envbuf);
1382 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
1383 grub_env_set("vtoy_chain_mem_size", envbuf);
1384
1385 grub_memset(chain, 0, sizeof(ventoy_chain_head));
1386
1387 /* part 1: os parameter */
1388 g_ventoy_chain_type = 1;
1389 ventoy_fill_os_param(file, &(chain->os_param));
1390
1391 if (0 == unknown_image)
1392 {
1393 ventoy_update_before_chain(&(chain->os_param), args[0]);
1394 }
1395
1396 /* part 2: chain head */
1397 disk = file->device->disk;
1398 chain->disk_drive = disk->id;
1399 chain->disk_sector_size = (1 << disk->log_sector_size);
1400 chain->real_img_size_in_bytes = file->size;
1401 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
1402 chain->boot_catalog = boot_catlog;
1403
1404 if (!ventoy_is_efi_os())
1405 {
1406 grub_file_seek(file, boot_catlog * 2048);
1407 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
1408 }
1409
1410 /* part 3: image chunk */
1411 chain->img_chunk_offset = sizeof(ventoy_chain_head);
1412 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
1413 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
1414
1415 if (ventoy_compatible || unknown_image)
1416 {
1417 if (g_suppress_wincd_override_offset > 0)
1418 {
1419 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
1420 chain->override_chunk_num = 1;
1421 ventoy_fill_suppress_wincd_override_data((char *)chain + chain->override_chunk_offset);
1422 }
1423
1424 return 0;
1425 }
1426
1427 if (0 == g_wim_valid_patch_count)
1428 {
1429 return 0;
1430 }
1431
1432 /* part 4: override chunk */
1433 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
1434 chain->override_chunk_num = ventoy_get_override_chunk_num();
1435
1436 if (g_iso_fs_type == 0)
1437 {
1438 ventoy_windows_fill_override_data_iso9660(isosize, (char *)chain + chain->override_chunk_offset);
1439 }
1440 else
1441 {
1442 ventoy_windows_fill_override_data_udf(isosize, (char *)chain + chain->override_chunk_offset);
1443 }
1444
1445 /* part 5: virt chunk */
1446 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
1447 chain->virt_chunk_num = g_wim_valid_patch_count;
1448 ventoy_windows_fill_virt_data(isosize, chain);
1449
1450 if (ventoy_is_efi_os() == 0)
1451 {
1452 ventoy_windows_drive_map(chain);
1453 }
1454
1455 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1456 }
1457
1458 static grub_uint32_t ventoy_get_wim_iso_offset(const char *filepath)
1459 {
1460 grub_uint32_t imgoffset;
1461 grub_file_t file;
1462 char cmdbuf[128];
1463
1464 grub_snprintf(cmdbuf, sizeof(cmdbuf), "loopback wimiso %s", filepath);
1465 grub_script_execute_sourcecode(cmdbuf);
1466
1467 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", "(wimiso)/boot/boot.wim");
1468 if (!file)
1469 {
1470 grub_printf("Failed to open boot.wim file in the image file\n");
1471 return 0;
1472 }
1473
1474 imgoffset = (grub_uint32_t)grub_iso9660_get_last_file_dirent_pos(file) + 2;
1475
1476 debug("wimiso wim direct offset: %u\n", imgoffset);
1477
1478 grub_file_close(file);
1479
1480 grub_script_execute_sourcecode("loopback -d wimiso");
1481
1482 return imgoffset;
1483 }
1484
1485 static int ventoy_get_wim_chunklist(const char *filename, ventoy_img_chunk_list *wimchunk, grub_uint64_t *wimsize)
1486 {
1487 grub_file_t wimfile;
1488
1489 wimfile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", filename);
1490 if (!wimfile)
1491 {
1492 return 1;
1493 }
1494
1495 grub_memset(wimchunk, 0, sizeof(ventoy_img_chunk_list));
1496 wimchunk->chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM);
1497 if (NULL == wimchunk->chunk)
1498 {
1499 grub_file_close(wimfile);
1500 return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n");
1501 }
1502
1503 wimchunk->max_chunk = DEFAULT_CHUNK_NUM;
1504 wimchunk->cur_chunk = 0;
1505
1506 ventoy_get_block_list(wimfile, wimchunk, wimfile->device->disk->partition->start);
1507
1508 *wimsize = wimfile->size;
1509 grub_file_close(wimfile);
1510
1511 return 0;
1512 }
1513
1514 grub_err_t ventoy_cmd_wim_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
1515 {
1516 grub_uint32_t i = 0;
1517 grub_uint32_t imgoffset = 0;
1518 grub_uint32_t size = 0;
1519 grub_uint32_t isosector = 0;
1520 grub_uint64_t wimsize = 0;
1521 grub_uint32_t boot_catlog = 0;
1522 grub_uint32_t img_chunk1_size = 0;
1523 grub_uint32_t img_chunk2_size = 0;
1524 grub_uint32_t override_size = 0;
1525 grub_file_t file;
1526 grub_disk_t disk;
1527 const char *pLastChain = NULL;
1528 ventoy_chain_head *chain;
1529 ventoy_iso9660_override *dirent;
1530 ventoy_img_chunk *chunknode;
1531 ventoy_override_chunk *override;
1532 ventoy_img_chunk_list wimchunk;
1533 char envbuf[128];
1534
1535 (void)ctxt;
1536 (void)argc;
1537
1538 debug("wim chain data begin <%s> ...\n", args[0]);
1539
1540 if (NULL == g_wimiso_chunk_list.chunk || NULL == g_wimiso_path)
1541 {
1542 grub_printf("ventoy not ready\n");
1543 return 1;
1544 }
1545
1546 imgoffset = ventoy_get_wim_iso_offset(g_wimiso_path);
1547 if (imgoffset == 0)
1548 {
1549 grub_printf("image offset not found\n");
1550 return 1;
1551 }
1552
1553 if (0 != ventoy_get_wim_chunklist(args[0], &wimchunk, &wimsize))
1554 {
1555 grub_printf("Failed to get wim chunklist\n");
1556 return 1;
1557 }
1558
1559 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", g_wimiso_path);
1560 if (!file)
1561 {
1562 return 1;
1563 }
1564
1565 boot_catlog = ventoy_get_iso_boot_catlog(file);
1566
1567 img_chunk1_size = g_wimiso_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
1568 img_chunk2_size = wimchunk.cur_chunk * sizeof(ventoy_img_chunk);
1569 override_size = sizeof(ventoy_override_chunk);
1570
1571 size = sizeof(ventoy_chain_head) + img_chunk1_size + img_chunk2_size + override_size;
1572
1573 pLastChain = grub_env_get("vtoy_chain_mem_addr");
1574 if (pLastChain)
1575 {
1576 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
1577 if (chain)
1578 {
1579 debug("free last chain memory %p\n", chain);
1580 grub_free(chain);
1581 }
1582 }
1583
1584 chain = grub_malloc(size);
1585 if (!chain)
1586 {
1587 grub_printf("Failed to alloc chain memory size %u\n", size);
1588 grub_file_close(file);
1589 return 1;
1590 }
1591
1592 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
1593 grub_env_set("vtoy_chain_mem_addr", envbuf);
1594 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
1595 grub_env_set("vtoy_chain_mem_size", envbuf);
1596
1597 grub_memset(chain, 0, sizeof(ventoy_chain_head));
1598
1599 /* part 1: os parameter */
1600 g_ventoy_chain_type = 0;
1601 ventoy_fill_os_param(file, &(chain->os_param));
1602
1603 /* part 2: chain head */
1604 disk = file->device->disk;
1605 chain->disk_drive = disk->id;
1606 chain->disk_sector_size = (1 << disk->log_sector_size);
1607 chain->real_img_size_in_bytes = ventoy_align_2k(file->size) + ventoy_align_2k(wimsize);
1608 chain->virt_img_size_in_bytes = chain->real_img_size_in_bytes;
1609 chain->boot_catalog = boot_catlog;
1610
1611 if (!ventoy_is_efi_os())
1612 {
1613 grub_file_seek(file, boot_catlog * 2048);
1614 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
1615 }
1616
1617 /* part 3: image chunk */
1618 chain->img_chunk_offset = sizeof(ventoy_chain_head);
1619 chain->img_chunk_num = g_wimiso_chunk_list.cur_chunk + wimchunk.cur_chunk;
1620 grub_memcpy((char *)chain + chain->img_chunk_offset, g_wimiso_chunk_list.chunk, img_chunk1_size);
1621
1622 /* fs cluster size >= 2048, so don't need to proc align */
1623
1624 /* align by 2048 */
1625 chunknode = wimchunk.chunk + wimchunk.cur_chunk - 1;
1626 i = (chunknode->disk_end_sector + 1 - chunknode->disk_start_sector) % 4;
1627 if (i)
1628 {
1629 chunknode->disk_end_sector += 4 - i;
1630 }
1631
1632 isosector = (grub_uint32_t)((file->size + 2047) / 2048);
1633 for (i = 0; i < wimchunk.cur_chunk; i++)
1634 {
1635 chunknode = wimchunk.chunk + i;
1636 chunknode->img_start_sector = isosector;
1637 chunknode->img_end_sector = chunknode->img_start_sector +
1638 ((chunknode->disk_end_sector + 1 - chunknode->disk_start_sector) / 4) - 1;
1639 isosector = chunknode->img_end_sector + 1;
1640 }
1641
1642 grub_memcpy((char *)chain + chain->img_chunk_offset + img_chunk1_size, wimchunk.chunk, img_chunk2_size);
1643
1644 /* part 4: override chunk */
1645 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk1_size + img_chunk2_size;
1646 chain->override_chunk_num = 1;
1647
1648 override = (ventoy_override_chunk *)((char *)chain + chain->override_chunk_offset);
1649 override->img_offset = imgoffset;
1650 override->override_size = sizeof(ventoy_iso9660_override);
1651
1652 dirent = (ventoy_iso9660_override *)(override->override_data);
1653 dirent->first_sector = (grub_uint32_t)((file->size + 2047) / 2048);
1654 dirent->size = (grub_uint32_t)(wimsize);
1655 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
1656 dirent->size_be = grub_swap_bytes32(dirent->size);
1657
1658 debug("imgoffset=%u first_sector=0x%x size=0x%x\n", imgoffset, dirent->first_sector, dirent->size);
1659
1660 if (ventoy_is_efi_os() == 0)
1661 {
1662 ventoy_windows_drive_map(chain);
1663 }
1664
1665 grub_file_close(file);
1666
1667 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1668 }
1669