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