]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/grub-2.04/grub-core/ventoy/ventoy_windows.c
1.0.07 release
[Ventoy.git] / GRUB2 / 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 wim_hash g_old_hash;
44 wim_tail g_wim_data;
45
46 static wim_lookup_entry *g_replace_look = NULL;
47
48 grub_ssize_t lzx_decompress ( const void *data, grub_size_t len, void *buf );
49
50 static int wim_name_cmp(const char *search, grub_uint16_t *name, grub_uint16_t namelen)
51 {
52 char c1 = vtoy_to_upper(*search);
53 char c2 = vtoy_to_upper(*name);
54
55 while (namelen > 0 && (c1 == c2))
56 {
57 search++;
58 name++;
59 namelen--;
60
61 c1 = vtoy_to_upper(*search);
62 c2 = vtoy_to_upper(*name);
63 }
64
65 if (namelen == 0 && *search == 0)
66 {
67 return 0;
68 }
69
70 return 1;
71 }
72
73 static int ventoy_is_pe64(grub_uint8_t *buffer)
74 {
75 grub_uint32_t pe_off;
76
77 if (buffer[0] != 'M' || buffer[1] != 'Z')
78 {
79 return 0;
80 }
81
82 pe_off = *(grub_uint32_t *)(buffer + 60);
83
84 if (buffer[pe_off] != 'P' || buffer[pe_off + 1] != 'E')
85 {
86 return 0;
87 }
88
89 if (*(grub_uint16_t *)(buffer + pe_off + 24) == 0x020b)
90 {
91 return 1;
92 }
93
94 return 0;
95 }
96
97 grub_err_t ventoy_cmd_wimdows_reset(grub_extcmd_context_t ctxt, int argc, char **args)
98 {
99 (void)ctxt;
100 (void)argc;
101 (void)args;
102
103 check_free(g_wim_data.jump_bin_data, grub_free);
104 check_free(g_wim_data.new_meta_data, grub_free);
105 check_free(g_wim_data.new_lookup_data, grub_free);
106
107 grub_memset(&g_wim_data, 0, sizeof(g_wim_data));
108
109 return 0;
110 }
111
112 static int ventoy_load_jump_exe(const char *path, grub_uint8_t **data, grub_uint32_t *size, wim_hash *hash)
113 {
114 grub_uint32_t i;
115 grub_uint32_t align;
116 grub_file_t file;
117
118 debug("windows load jump %s\n", path);
119
120 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", path);
121 if (!file)
122 {
123 debug("Can't open file %s\n", path);
124 return 1;
125 }
126
127 align = ventoy_align((int)file->size, 2048);
128
129 debug("file %s size:%d align:%u\n", path, (int)file->size, align);
130
131 *size = (grub_uint32_t)file->size;
132 *data = (grub_uint8_t *)grub_malloc(align);
133 if ((*data) == NULL)
134 {
135 debug("Failed to alloc memory size %u\n", align);
136 goto end;
137 }
138
139 grub_file_read(file, (*data), file->size);
140
141 if (hash)
142 {
143 grub_crypto_hash(GRUB_MD_SHA1, hash->sha1, (*data), file->size);
144
145 if (g_ventoy_debug)
146 {
147 debug("%s", "jump bin 64 hash: ");
148 for (i = 0; i < sizeof(hash->sha1); i++)
149 {
150 ventoy_debug("%02x ", hash->sha1[i]);
151 }
152 ventoy_debug("\n");
153 }
154 }
155
156 end:
157
158 grub_file_close(file);
159 return 0;
160 }
161
162 static int ventoy_get_override_info(grub_file_t file)
163 {
164 grub_uint32_t start_block;
165 grub_uint64_t file_offset;
166 grub_uint64_t override_offset;
167 grub_uint32_t override_len;
168 grub_uint64_t fe_entry_size_offset;
169
170 if (grub_strcmp(file->fs->name, "iso9660") == 0)
171 {
172 g_wim_data.iso_type = 0;
173 override_len = sizeof(ventoy_iso9660_override);
174 override_offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
175
176 grub_file_read(file, &start_block, 1); // just read for hook trigger
177 file_offset = grub_iso9660_get_last_read_pos(file);
178
179 debug("iso9660 wim size:%llu override_offset:%llu file_offset:%llu\n",
180 (ulonglong)file->size, (ulonglong)override_offset, (ulonglong)file_offset);
181 }
182 else
183 {
184 g_wim_data.iso_type = 1;
185 override_len = sizeof(ventoy_udf_override);
186 override_offset = grub_udf_get_last_file_attr_offset(file, &start_block, &fe_entry_size_offset);
187
188 file_offset = grub_udf_get_file_offset(file);
189
190 debug("UDF wim size:%llu override_offset:%llu file_offset:%llu start_block=%u\n",
191 (ulonglong)file->size, (ulonglong)override_offset, (ulonglong)file_offset, start_block);
192 }
193
194 g_wim_data.file_offset = file_offset;
195 g_wim_data.udf_start_block = start_block;
196 g_wim_data.fe_entry_size_offset = fe_entry_size_offset;
197 g_wim_data.override_offset = override_offset;
198 g_wim_data.override_len = override_len;
199
200 return 0;
201 }
202
203 static int ventoy_read_resource(grub_file_t fp, wim_resource_header *head, void **buffer)
204 {
205 int decompress_len = 0;
206 int total_decompress = 0;
207 grub_uint32_t i = 0;
208 grub_uint32_t chunk_num = 0;
209 grub_uint32_t chunk_size = 0;
210 grub_uint32_t last_chunk_size = 0;
211 grub_uint32_t last_decompress_size = 0;
212 grub_uint32_t cur_offset = 0;
213 grub_uint8_t *cur_dst = NULL;
214 grub_uint8_t *buffer_compress = NULL;
215 grub_uint8_t *buffer_decompress = NULL;
216 grub_uint32_t *chunk_offset = NULL;
217
218 buffer_decompress = (grub_uint8_t *)grub_malloc(head->raw_size + head->size_in_wim);
219 if (NULL == buffer_decompress)
220 {
221 return 0;
222 }
223
224 grub_file_seek(fp, head->offset);
225
226 if (head->size_in_wim == head->raw_size)
227 {
228 grub_file_read(fp, buffer_decompress, head->size_in_wim);
229 *buffer = buffer_decompress;
230 return 0;
231 }
232
233 buffer_compress = buffer_decompress + head->raw_size;
234 grub_file_read(fp, buffer_compress, head->size_in_wim);
235
236 chunk_num = (head->raw_size + WIM_CHUNK_LEN - 1) / WIM_CHUNK_LEN;
237 cur_offset = (chunk_num - 1) * 4;
238 chunk_offset = (grub_uint32_t *)buffer_compress;
239
240 cur_dst = buffer_decompress;
241
242 for (i = 0; i < chunk_num - 1; i++)
243 {
244 chunk_size = (i == 0) ? chunk_offset[i] : chunk_offset[i] - chunk_offset[i - 1];
245
246 if (WIM_CHUNK_LEN == chunk_size)
247 {
248 grub_memcpy(cur_dst, buffer_compress + cur_offset, chunk_size);
249 decompress_len = (int)chunk_size;
250 }
251 else
252 {
253 decompress_len = (int)lzx_decompress(buffer_compress + cur_offset, chunk_size, cur_dst);
254 }
255
256 //debug("chunk_size:%u decompresslen:%d\n", chunk_size, decompress_len);
257
258 total_decompress += decompress_len;
259 cur_dst += decompress_len;
260 cur_offset += chunk_size;
261 }
262
263 /* last chunk */
264 last_chunk_size = (grub_uint32_t)(head->size_in_wim - cur_offset);
265 last_decompress_size = head->raw_size - total_decompress;
266
267 if (last_chunk_size < WIM_CHUNK_LEN && last_chunk_size == last_decompress_size)
268 {
269 debug("Last chunk %u uncompressed\n", last_chunk_size);
270 grub_memcpy(cur_dst, buffer_compress + cur_offset, last_chunk_size);
271 decompress_len = (int)last_chunk_size;
272 }
273 else
274 {
275 decompress_len = (int)lzx_decompress(buffer_compress + cur_offset, head->size_in_wim - cur_offset, cur_dst);
276 }
277
278 cur_dst += decompress_len;
279 total_decompress += decompress_len;
280
281 if (cur_dst != buffer_decompress + head->raw_size)
282 {
283 debug("head->size_in_wim:%llu head->raw_size:%llu cur_dst:%p buffer_decompress:%p total_decompress:%d\n",
284 (ulonglong)head->size_in_wim, (ulonglong)head->raw_size, cur_dst, buffer_decompress, total_decompress);
285 grub_free(buffer_decompress);
286 return 1;
287 }
288
289 *buffer = buffer_decompress;
290 return 0;
291 }
292
293
294 static wim_directory_entry * search_wim_dirent(wim_directory_entry *dir, const char *search_name)
295 {
296 do
297 {
298 if (dir->len && dir->name_len)
299 {
300 if (wim_name_cmp(search_name, (grub_uint16_t *)(dir + 1), dir->name_len / 2) == 0)
301 {
302 return dir;
303 }
304 }
305 dir = (wim_directory_entry *)((grub_uint8_t *)dir + dir->len);
306 } while(dir->len);
307
308 return NULL;
309 }
310
311 static wim_directory_entry * search_full_wim_dirent
312 (
313 void *meta_data,
314 wim_directory_entry *dir,
315 const char **path
316 )
317 {
318 wim_directory_entry *subdir = NULL;
319 wim_directory_entry *search = dir;
320
321 while (*path)
322 {
323 subdir = (wim_directory_entry *)((char *)meta_data + search->subdir);
324 search = search_wim_dirent(subdir, *path);
325 if (!search)
326 {
327 debug("%s search failed\n", *path);
328 }
329
330 path++;
331 }
332 return search;
333 }
334
335 static wim_directory_entry * search_replace_wim_dirent(void *meta_data, wim_directory_entry *dir)
336 {
337 wim_directory_entry *wim_dirent = NULL;
338 const char *winpeshl_path[] = { "Windows", "System32", "winpeshl.exe", NULL };
339 const char *pecmd_path[] = { "Windows", "System32", "PECMD.exe", NULL };
340
341 wim_dirent = search_full_wim_dirent(meta_data, dir, winpeshl_path);
342 if (wim_dirent)
343 {
344 return wim_dirent;
345 }
346
347 wim_dirent = search_full_wim_dirent(meta_data, dir, pecmd_path);
348 if (wim_dirent)
349 {
350 return wim_dirent;
351 }
352
353 return NULL;
354 }
355
356
357 static wim_lookup_entry * ventoy_find_look_entry(wim_header *header, wim_lookup_entry *lookup, wim_hash *hash)
358 {
359 grub_uint32_t i = 0;
360
361 for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++)
362 {
363 if (grub_memcmp(&lookup[i].hash, hash, sizeof(wim_hash)) == 0)
364 {
365 return lookup + i;
366 }
367 }
368
369 return NULL;
370 }
371
372 static wim_lookup_entry * ventoy_find_meta_entry(wim_header *header, wim_lookup_entry *lookup)
373 {
374 grub_uint32_t i = 0;
375 grub_uint32_t index = 0;;
376
377 if ((header == NULL) || (lookup == NULL))
378 {
379 return NULL;
380 }
381
382 for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++)
383 {
384 if (lookup[i].resource.flags & RESHDR_FLAG_METADATA)
385 {
386 index++;
387 if (index == header->boot_index)
388 {
389 return lookup + i;
390 }
391 }
392 }
393
394 return NULL;
395 }
396
397 static int ventoy_update_all_hash(void *meta_data, wim_directory_entry *dir)
398 {
399 if ((meta_data == NULL) || (dir == NULL))
400 {
401 return 0;
402 }
403
404 if (dir->len == 0)
405 {
406 return 0;
407 }
408
409 do
410 {
411 if (dir->subdir == 0 && grub_memcmp(dir->hash.sha1, g_old_hash.sha1, sizeof(wim_hash)) == 0)
412 {
413 debug("find target file, name_len:%u upadte hash\n", dir->name_len);
414 grub_memcpy(dir->hash.sha1, &(g_wim_data.bin_hash), sizeof(wim_hash));
415 }
416
417 if (dir->subdir)
418 {
419 ventoy_update_all_hash(meta_data, (wim_directory_entry *)((char *)meta_data + dir->subdir));
420 }
421
422 dir = (wim_directory_entry *)((char *)dir + dir->len);
423 } while (dir->len);
424
425 return 0;
426 }
427
428 static int ventoy_cat_exe_file_data(grub_uint32_t exe_len, grub_uint8_t *exe_data)
429 {
430 int pe64 = 0;
431 char file[256];
432 grub_uint32_t jump_len = 0;
433 grub_uint32_t jump_align = 0;
434 grub_uint8_t *jump_data = NULL;
435
436 pe64 = ventoy_is_pe64(exe_data);
437
438 grub_snprintf(file, sizeof(file), "%s/vtoyjump%d.exe", grub_env_get("vtoy_path"), pe64 ? 64 : 32);
439 ventoy_load_jump_exe(file, &jump_data, &jump_len, NULL);
440 jump_align = ventoy_align(jump_len, 16);
441
442 g_wim_data.jump_exe_len = jump_len;
443 g_wim_data.bin_raw_len = jump_align + sizeof(ventoy_os_param) + exe_len;
444 g_wim_data.bin_align_len = ventoy_align(g_wim_data.bin_raw_len, 2048);
445
446 g_wim_data.jump_bin_data = grub_malloc(g_wim_data.bin_align_len);
447 if (g_wim_data.jump_bin_data)
448 {
449 grub_memcpy(g_wim_data.jump_bin_data, jump_data, jump_len);
450 grub_memcpy(g_wim_data.jump_bin_data + jump_align + sizeof(ventoy_os_param), exe_data, exe_len);
451 }
452
453 debug("jump_exe_len:%u bin_raw_len:%u bin_align_len:%u\n",
454 g_wim_data.jump_exe_len, g_wim_data.bin_raw_len, g_wim_data.bin_align_len);
455
456 return 0;
457 }
458
459 static int ventoy_update_before_chain(ventoy_os_param *param)
460 {
461 grub_uint32_t jump_align = 0;
462 wim_lookup_entry *meta_look = NULL;
463 wim_security_header *security = NULL;
464 wim_directory_entry *rootdir = NULL;
465 wim_header *head = &(g_wim_data.wim_header);
466 wim_lookup_entry *lookup = (wim_lookup_entry *)g_wim_data.new_lookup_data;
467
468 jump_align = ventoy_align(g_wim_data.jump_exe_len, 16);
469 if (g_wim_data.jump_bin_data)
470 {
471 grub_memcpy(g_wim_data.jump_bin_data + jump_align, param, sizeof(ventoy_os_param));
472 }
473
474 grub_crypto_hash(GRUB_MD_SHA1, g_wim_data.bin_hash.sha1, g_wim_data.jump_bin_data, g_wim_data.bin_raw_len);
475
476 security = (wim_security_header *)g_wim_data.new_meta_data;
477 rootdir = (wim_directory_entry *)(g_wim_data.new_meta_data + ((security->len + 7) & 0xFFFFFFF8U));
478
479 /* update all winpeshl.exe dirent entry's hash */
480 ventoy_update_all_hash(g_wim_data.new_meta_data, rootdir);
481
482 /* update winpeshl.exe lookup entry data (hash/offset/length) */
483 if (g_replace_look)
484 {
485 debug("update replace lookup entry_id:%ld\n", ((long)g_replace_look - (long)lookup) / sizeof(wim_lookup_entry));
486 g_replace_look->resource.raw_size = g_wim_data.bin_raw_len;
487 g_replace_look->resource.size_in_wim = g_wim_data.bin_raw_len;
488 g_replace_look->resource.flags = 0;
489 g_replace_look->resource.offset = g_wim_data.wim_align_size;
490
491 grub_memcpy(g_replace_look->hash.sha1, g_wim_data.bin_hash.sha1, sizeof(wim_hash));
492 }
493
494 /* update metadata's hash */
495 meta_look = ventoy_find_meta_entry(head, lookup);
496 if (meta_look)
497 {
498 debug("find meta lookup entry_id:%ld\n", ((long)meta_look - (long)lookup) / sizeof(wim_lookup_entry));
499 grub_memcpy(&meta_look->resource, &head->metadata, sizeof(wim_resource_header));
500 grub_crypto_hash(GRUB_MD_SHA1, meta_look->hash.sha1, g_wim_data.new_meta_data, g_wim_data.new_meta_len);
501 }
502
503 return 0;
504 }
505
506 grub_err_t ventoy_cmd_wimdows_locate_wim(grub_extcmd_context_t ctxt, int argc, char **args)
507 {
508 int rc;
509 grub_file_t file;
510 grub_uint32_t exe_len;
511 grub_uint8_t *exe_data = NULL;
512 grub_uint8_t *decompress_data = NULL;
513 wim_lookup_entry *lookup = NULL;
514 wim_security_header *security = NULL;
515 wim_directory_entry *rootdir = NULL;
516 wim_directory_entry *search = NULL;
517 wim_header *head = &(g_wim_data.wim_header);
518
519 (void)ctxt;
520 (void)argc;
521
522 debug("windows locate wim start %s\n", args[0]);
523
524 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
525 if (!file)
526 {
527 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]);
528 }
529
530 ventoy_get_override_info(file);
531
532 grub_file_seek(file, 0);
533 grub_file_read(file, head, sizeof(wim_header));
534
535 if (grub_memcmp(head->signature, WIM_HEAD_SIGNATURE, sizeof(head->signature)))
536 {
537 debug("Not a valid wim file %s\n", (char *)head->signature);
538 grub_file_close(file);
539 return 1;
540 }
541
542 if (head->flags & FLAG_HEADER_COMPRESS_XPRESS)
543 {
544 debug("Xpress compress is not supported 0x%x\n", head->flags);
545 grub_file_close(file);
546 return 1;
547 }
548
549 rc = ventoy_read_resource(file, &head->metadata, (void **)&decompress_data);
550 if (rc)
551 {
552 grub_printf("failed to read meta data %d\n", rc);
553 grub_file_close(file);
554 return 1;
555 }
556
557 security = (wim_security_header *)decompress_data;
558 rootdir = (wim_directory_entry *)(decompress_data + ((security->len + 7) & 0xFFFFFFF8U));
559
560 /* search winpeshl.exe dirent entry */
561 search = search_replace_wim_dirent(decompress_data, rootdir);
562 if (!search)
563 {
564 debug("Failed to find replace file %p\n", search);
565 grub_file_close(file);
566 return 1;
567 }
568
569 debug("find replace file at %p\n", search);
570
571 grub_memcpy(&g_old_hash, search->hash.sha1, sizeof(wim_hash));
572
573 debug("read lookup offset:%llu size:%llu\n", (ulonglong)head->lookup.offset, (ulonglong)head->lookup.raw_size);
574 lookup = grub_malloc(head->lookup.raw_size);
575 grub_file_seek(file, head->lookup.offset);
576 grub_file_read(file, lookup, head->lookup.raw_size);
577
578 /* find and extact winpeshl.exe */
579 g_replace_look = ventoy_find_look_entry(head, lookup, &g_old_hash);
580 if (g_replace_look)
581 {
582 exe_len = (grub_uint32_t)g_replace_look->resource.raw_size;
583 debug("find replace lookup entry_id:%ld raw_size:%u\n",
584 ((long)g_replace_look - (long)lookup) / sizeof(wim_lookup_entry), exe_len);
585
586 if (0 == ventoy_read_resource(file, &(g_replace_look->resource), (void **)&(exe_data)))
587 {
588 ventoy_cat_exe_file_data(exe_len, exe_data);
589 grub_free(exe_data);
590 }
591 else
592 {
593 debug("failed to read replace file meta data %u\n", exe_len);
594 }
595 }
596 else
597 {
598 debug("failed to find lookup entry for replace file 0x%02x 0x%02x\n", g_old_hash.sha1[0], g_old_hash.sha1[1]);
599 }
600
601 g_wim_data.wim_raw_size = (grub_uint32_t)file->size;
602 g_wim_data.wim_align_size = ventoy_align(g_wim_data.wim_raw_size, 2048);
603
604 check_free(g_wim_data.new_meta_data, grub_free);
605 g_wim_data.new_meta_data = decompress_data;
606 g_wim_data.new_meta_len = head->metadata.raw_size;
607 g_wim_data.new_meta_align_len = ventoy_align(g_wim_data.new_meta_len, 2048);
608
609 check_free(g_wim_data.new_lookup_data, grub_free);
610 g_wim_data.new_lookup_data = (grub_uint8_t *)lookup;
611 g_wim_data.new_lookup_len = (grub_uint32_t)head->lookup.raw_size;
612 g_wim_data.new_lookup_align_len = ventoy_align(g_wim_data.new_lookup_len, 2048);
613
614 head->metadata.flags = RESHDR_FLAG_METADATA;
615 head->metadata.offset = g_wim_data.wim_align_size + g_wim_data.bin_align_len;
616 head->metadata.size_in_wim = g_wim_data.new_meta_len;
617 head->metadata.raw_size = g_wim_data.new_meta_len;
618
619 head->lookup.flags = 0;
620 head->lookup.offset = head->metadata.offset + g_wim_data.new_meta_align_len;
621 head->lookup.size_in_wim = g_wim_data.new_lookup_len;
622 head->lookup.raw_size = g_wim_data.new_lookup_len;
623
624 grub_file_close(file);
625
626 debug("%s", "windows locate wim finish\n");
627 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
628 }
629
630 static grub_uint32_t ventoy_get_override_chunk_num(void)
631 {
632 /* 1: block count in Partition Descriptor */
633 /* 2: file_size in file_entry or extend_file_entry */
634 /* 3: data_size and position in extend data short ad */
635 /* 4: new wim file header */
636 return 4;
637 }
638
639 static void ventoy_windows_fill_override_data( grub_uint64_t isosize, void *override)
640 {
641 grub_uint32_t data32;
642 grub_uint64_t data64;
643 grub_uint64_t sector;
644 grub_uint32_t new_wim_size;
645 ventoy_override_chunk *cur;
646
647 sector = (isosize + 2047) / 2048;
648
649 cur = (ventoy_override_chunk *)override;
650
651 new_wim_size = g_wim_data.wim_align_size + g_wim_data.bin_align_len +
652 g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len;
653
654 if (g_wim_data.iso_type == 0)
655 {
656 ventoy_iso9660_override *dirent = (ventoy_iso9660_override *)g_wim_data.override_data;
657
658 dirent->first_sector = (grub_uint32_t)sector;
659 dirent->size = new_wim_size;
660 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
661 dirent->size_be = grub_swap_bytes32(dirent->size);
662 }
663 else
664 {
665 ventoy_udf_override *udf = (ventoy_udf_override *)g_wim_data.override_data;
666 udf->length = new_wim_size;
667 udf->position = (grub_uint32_t)sector - g_wim_data.udf_start_block;
668 }
669
670 //override 1: sector number in pd data
671 cur->img_offset = grub_udf_get_last_pd_size_offset();
672 cur->override_size = 4;
673 data32 = sector - g_wim_data.udf_start_block + (new_wim_size / 2048);
674 grub_memcpy(cur->override_data, &(data32), 4);
675
676 //override 2: filesize in file_entry
677 cur++;
678 cur->img_offset = g_wim_data.fe_entry_size_offset;
679 cur->override_size = 8;
680 data64 = new_wim_size;
681 grub_memcpy(cur->override_data, &(data64), 8);
682
683 /* override 3: position and length in extend data */
684 cur++;
685 cur->img_offset = g_wim_data.override_offset;
686 cur->override_size = g_wim_data.override_len;
687 grub_memcpy(cur->override_data, g_wim_data.override_data, cur->override_size);
688
689 /* override 4: new wim file header */
690 cur++;
691 cur->img_offset = g_wim_data.file_offset;
692 cur->override_size = sizeof(wim_header);
693 grub_memcpy(cur->override_data, &(g_wim_data.wim_header), cur->override_size);
694
695 return;
696 }
697
698 static void ventoy_windows_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
699 {
700 grub_uint64_t sector;
701 grub_uint32_t offset;
702 grub_uint32_t wim_secs;
703 grub_uint32_t mem_secs;
704 char *override = NULL;
705 ventoy_virt_chunk *cur = NULL;
706
707 sector = (isosize + 2047) / 2048;
708 offset = sizeof(ventoy_virt_chunk);
709
710 wim_secs = g_wim_data.wim_align_size / 2048;
711 mem_secs = (g_wim_data.bin_align_len + g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len) / 2048;
712
713 override = (char *)chain + chain->virt_chunk_offset;
714 cur = (ventoy_virt_chunk *)override;
715
716 cur->remap_sector_start = sector;
717 cur->remap_sector_end = cur->remap_sector_start + wim_secs;
718 cur->org_sector_start = (grub_uint32_t)(g_wim_data.file_offset / 2048);
719
720 cur->mem_sector_start = cur->remap_sector_end;
721 cur->mem_sector_end = cur->mem_sector_start + mem_secs;
722 cur->mem_sector_offset = offset;
723
724 grub_memcpy(override + offset, g_wim_data.jump_bin_data, g_wim_data.bin_raw_len);
725 offset += g_wim_data.bin_align_len;
726
727 grub_memcpy(override + offset, g_wim_data.new_meta_data, g_wim_data.new_meta_len);
728 offset += g_wim_data.new_meta_align_len;
729
730 grub_memcpy(override + offset, g_wim_data.new_lookup_data, g_wim_data.new_lookup_len);
731 offset += g_wim_data.new_lookup_align_len;
732
733 chain->virt_img_size_in_bytes += g_wim_data.wim_align_size +
734 g_wim_data.bin_align_len +
735 g_wim_data.new_meta_align_len +
736 g_wim_data.new_lookup_align_len;
737 return;
738 }
739
740 static int ventoy_windows_drive_map(ventoy_chain_head *chain)
741 {
742 grub_disk_t disk;
743
744 debug("drive map begin <%p> ...\n", chain);
745
746 if (chain->disk_drive == 0x80)
747 {
748 disk = grub_disk_open("hd1");
749 if (disk)
750 {
751 grub_disk_close(disk);
752 debug("drive map needed %p\n", disk);
753 chain->drive_map = 0x81;
754 }
755 else
756 {
757 debug("failed to open disk %s\n", "hd1");
758 }
759 }
760 else
761 {
762 debug("no need to map 0x%x\n", chain->disk_drive);
763 }
764
765 return 0;
766 }
767
768 grub_err_t ventoy_cmd_windows_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
769 {
770 int unknown_image = 0;
771 int ventoy_compatible = 0;
772 grub_uint32_t size = 0;
773 grub_uint64_t isosize = 0;
774 grub_uint32_t boot_catlog = 0;
775 grub_uint32_t img_chunk_size = 0;
776 grub_uint32_t override_size = 0;
777 grub_uint32_t virt_chunk_size = 0;
778 grub_file_t file;
779 grub_disk_t disk;
780 const char *pLastChain = NULL;
781 const char *compatible;
782 ventoy_chain_head *chain;
783 char envbuf[64];
784
785 (void)ctxt;
786 (void)argc;
787
788 debug("chain data begin <%s> ...\n", args[0]);
789
790 compatible = grub_env_get("ventoy_compatible");
791 if (compatible && compatible[0] == 'Y')
792 {
793 ventoy_compatible = 1;
794 }
795
796 if (NULL == g_img_chunk_list.chunk)
797 {
798 grub_printf("ventoy not ready\n");
799 return 1;
800 }
801
802 if (0 == ventoy_compatible && g_wim_data.new_meta_data == NULL)
803 {
804 unknown_image = 1;
805 debug("Warning: %s was not recognized by Ventoy\n", args[0]);
806 }
807
808 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
809 if (!file)
810 {
811 return 1;
812 }
813
814 isosize = file->size;
815
816 boot_catlog = ventoy_get_iso_boot_catlog(file);
817 if (boot_catlog)
818 {
819 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
820 {
821 grub_env_set("LoadIsoEfiDriver", "on");
822 }
823 }
824 else
825 {
826 if (ventoy_is_efi_os())
827 {
828 grub_env_set("LoadIsoEfiDriver", "on");
829 }
830 else
831 {
832 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
833 }
834 }
835
836 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
837
838 if (ventoy_compatible || unknown_image)
839 {
840 size = sizeof(ventoy_chain_head) + img_chunk_size;
841 }
842 else
843 {
844 override_size = ventoy_get_override_chunk_num() * sizeof(ventoy_override_chunk);
845 virt_chunk_size = sizeof(ventoy_virt_chunk) + g_wim_data.bin_align_len +
846 g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len;;
847 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
848 }
849
850 pLastChain = grub_env_get("vtoy_chain_mem_addr");
851 if (pLastChain)
852 {
853 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
854 if (chain)
855 {
856 debug("free last chain memory %p\n", chain);
857 grub_free(chain);
858 }
859 }
860
861 chain = grub_malloc(size);
862 if (!chain)
863 {
864 grub_printf("Failed to alloc chain memory size %u\n", size);
865 grub_file_close(file);
866 return 1;
867 }
868
869 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
870 grub_env_set("vtoy_chain_mem_addr", envbuf);
871 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
872 grub_env_set("vtoy_chain_mem_size", envbuf);
873
874 grub_memset(chain, 0, sizeof(ventoy_chain_head));
875
876 /* part 1: os parameter */
877 ventoy_fill_os_param(file, &(chain->os_param));
878
879 if (g_wim_data.jump_bin_data && g_wim_data.new_meta_data)
880 {
881 ventoy_update_before_chain(&(chain->os_param));
882 }
883
884 /* part 2: chain head */
885 disk = file->device->disk;
886 chain->disk_drive = disk->id;
887 chain->disk_sector_size = (1 << disk->log_sector_size);
888 chain->real_img_size_in_bytes = file->size;
889 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
890 chain->boot_catalog = boot_catlog;
891
892 if (!ventoy_is_efi_os())
893 {
894 grub_file_seek(file, boot_catlog * 2048);
895 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
896 }
897
898 /* part 3: image chunk */
899 chain->img_chunk_offset = sizeof(ventoy_chain_head);
900 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
901 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
902
903 if (ventoy_compatible || unknown_image)
904 {
905 return 0;
906 }
907
908 if (g_wim_data.new_meta_data == NULL)
909 {
910 return 0;
911 }
912
913 /* part 4: override chunk */
914 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
915 chain->override_chunk_num = ventoy_get_override_chunk_num();
916 ventoy_windows_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
917
918 /* part 5: virt chunk */
919 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
920 chain->virt_chunk_num = 1;
921 ventoy_windows_fill_virt_data(isosize, chain);
922
923 if (ventoy_is_efi_os() == 0)
924 {
925 ventoy_windows_drive_map(chain);
926 }
927
928 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
929 }
930
931