]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/grub-2.04/grub-core/ventoy/ventoy_windows.c
e2b4eed9de2c1ad65a5f8b0ddd8a93aed1ee33b5
[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) + sizeof(ventoy_windows_data) + 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) + sizeof(ventoy_windows_data), 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 int ventoy_fill_windows_rtdata(void *buf, char *isopath)
460 {
461 char *pos = NULL;
462 char *script = NULL;
463 ventoy_windows_data *data = (ventoy_windows_data *)buf;
464
465 grub_memset(data, 0, sizeof(ventoy_windows_data));
466
467 pos = grub_strstr(isopath, "/");
468 if (!pos)
469 {
470 return 1;
471 }
472
473 script = ventoy_plugin_get_install_template(pos);
474 if (script)
475 {
476 debug("auto install script <%s>\n", script);
477 grub_snprintf(data->auto_install_script, sizeof(data->auto_install_script) - 1, "%s", script);
478 }
479 else
480 {
481 debug("auto install script not found %p\n", pos);
482 }
483
484 return 0;
485 }
486
487 static int ventoy_update_before_chain(ventoy_os_param *param, char *isopath)
488 {
489 grub_uint32_t jump_align = 0;
490 wim_lookup_entry *meta_look = NULL;
491 wim_security_header *security = NULL;
492 wim_directory_entry *rootdir = NULL;
493 wim_header *head = &(g_wim_data.wim_header);
494 wim_lookup_entry *lookup = (wim_lookup_entry *)g_wim_data.new_lookup_data;
495
496 jump_align = ventoy_align(g_wim_data.jump_exe_len, 16);
497 if (g_wim_data.jump_bin_data)
498 {
499 grub_memcpy(g_wim_data.jump_bin_data + jump_align, param, sizeof(ventoy_os_param));
500 ventoy_fill_windows_rtdata(g_wim_data.jump_bin_data + jump_align + sizeof(ventoy_os_param), isopath);
501 }
502
503 grub_crypto_hash(GRUB_MD_SHA1, g_wim_data.bin_hash.sha1, g_wim_data.jump_bin_data, g_wim_data.bin_raw_len);
504
505 security = (wim_security_header *)g_wim_data.new_meta_data;
506 rootdir = (wim_directory_entry *)(g_wim_data.new_meta_data + ((security->len + 7) & 0xFFFFFFF8U));
507
508 /* update all winpeshl.exe dirent entry's hash */
509 ventoy_update_all_hash(g_wim_data.new_meta_data, rootdir);
510
511 /* update winpeshl.exe lookup entry data (hash/offset/length) */
512 if (g_replace_look)
513 {
514 debug("update replace lookup entry_id:%ld\n", ((long)g_replace_look - (long)lookup) / sizeof(wim_lookup_entry));
515 g_replace_look->resource.raw_size = g_wim_data.bin_raw_len;
516 g_replace_look->resource.size_in_wim = g_wim_data.bin_raw_len;
517 g_replace_look->resource.flags = 0;
518 g_replace_look->resource.offset = g_wim_data.wim_align_size;
519
520 grub_memcpy(g_replace_look->hash.sha1, g_wim_data.bin_hash.sha1, sizeof(wim_hash));
521 }
522
523 /* update metadata's hash */
524 meta_look = ventoy_find_meta_entry(head, lookup);
525 if (meta_look)
526 {
527 debug("find meta lookup entry_id:%ld\n", ((long)meta_look - (long)lookup) / sizeof(wim_lookup_entry));
528 grub_memcpy(&meta_look->resource, &head->metadata, sizeof(wim_resource_header));
529 grub_crypto_hash(GRUB_MD_SHA1, meta_look->hash.sha1, g_wim_data.new_meta_data, g_wim_data.new_meta_len);
530 }
531
532 return 0;
533 }
534
535 grub_err_t ventoy_cmd_wimdows_locate_wim(grub_extcmd_context_t ctxt, int argc, char **args)
536 {
537 int rc;
538 grub_file_t file;
539 grub_uint32_t exe_len;
540 grub_uint8_t *exe_data = NULL;
541 grub_uint8_t *decompress_data = NULL;
542 wim_lookup_entry *lookup = NULL;
543 wim_security_header *security = NULL;
544 wim_directory_entry *rootdir = NULL;
545 wim_directory_entry *search = NULL;
546 wim_header *head = &(g_wim_data.wim_header);
547
548 (void)ctxt;
549 (void)argc;
550
551 debug("windows locate wim start %s\n", args[0]);
552
553 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
554 if (!file)
555 {
556 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]);
557 }
558
559 ventoy_get_override_info(file);
560
561 grub_file_seek(file, 0);
562 grub_file_read(file, head, sizeof(wim_header));
563
564 if (grub_memcmp(head->signature, WIM_HEAD_SIGNATURE, sizeof(head->signature)))
565 {
566 debug("Not a valid wim file %s\n", (char *)head->signature);
567 grub_file_close(file);
568 return 1;
569 }
570
571 if (head->flags & FLAG_HEADER_COMPRESS_XPRESS)
572 {
573 debug("Xpress compress is not supported 0x%x\n", head->flags);
574 grub_file_close(file);
575 return 1;
576 }
577
578 rc = ventoy_read_resource(file, &head->metadata, (void **)&decompress_data);
579 if (rc)
580 {
581 grub_printf("failed to read meta data %d\n", rc);
582 grub_file_close(file);
583 return 1;
584 }
585
586 security = (wim_security_header *)decompress_data;
587 rootdir = (wim_directory_entry *)(decompress_data + ((security->len + 7) & 0xFFFFFFF8U));
588
589 /* search winpeshl.exe dirent entry */
590 search = search_replace_wim_dirent(decompress_data, rootdir);
591 if (!search)
592 {
593 debug("Failed to find replace file %p\n", search);
594 grub_file_close(file);
595 return 1;
596 }
597
598 debug("find replace file at %p\n", search);
599
600 grub_memcpy(&g_old_hash, search->hash.sha1, sizeof(wim_hash));
601
602 debug("read lookup offset:%llu size:%llu\n", (ulonglong)head->lookup.offset, (ulonglong)head->lookup.raw_size);
603 lookup = grub_malloc(head->lookup.raw_size);
604 grub_file_seek(file, head->lookup.offset);
605 grub_file_read(file, lookup, head->lookup.raw_size);
606
607 /* find and extact winpeshl.exe */
608 g_replace_look = ventoy_find_look_entry(head, lookup, &g_old_hash);
609 if (g_replace_look)
610 {
611 exe_len = (grub_uint32_t)g_replace_look->resource.raw_size;
612 debug("find replace lookup entry_id:%ld raw_size:%u\n",
613 ((long)g_replace_look - (long)lookup) / sizeof(wim_lookup_entry), exe_len);
614
615 if (0 == ventoy_read_resource(file, &(g_replace_look->resource), (void **)&(exe_data)))
616 {
617 ventoy_cat_exe_file_data(exe_len, exe_data);
618 grub_free(exe_data);
619 }
620 else
621 {
622 debug("failed to read replace file meta data %u\n", exe_len);
623 }
624 }
625 else
626 {
627 debug("failed to find lookup entry for replace file 0x%02x 0x%02x\n", g_old_hash.sha1[0], g_old_hash.sha1[1]);
628 }
629
630 g_wim_data.wim_raw_size = (grub_uint32_t)file->size;
631 g_wim_data.wim_align_size = ventoy_align(g_wim_data.wim_raw_size, 2048);
632
633 check_free(g_wim_data.new_meta_data, grub_free);
634 g_wim_data.new_meta_data = decompress_data;
635 g_wim_data.new_meta_len = head->metadata.raw_size;
636 g_wim_data.new_meta_align_len = ventoy_align(g_wim_data.new_meta_len, 2048);
637
638 check_free(g_wim_data.new_lookup_data, grub_free);
639 g_wim_data.new_lookup_data = (grub_uint8_t *)lookup;
640 g_wim_data.new_lookup_len = (grub_uint32_t)head->lookup.raw_size;
641 g_wim_data.new_lookup_align_len = ventoy_align(g_wim_data.new_lookup_len, 2048);
642
643 head->metadata.flags = RESHDR_FLAG_METADATA;
644 head->metadata.offset = g_wim_data.wim_align_size + g_wim_data.bin_align_len;
645 head->metadata.size_in_wim = g_wim_data.new_meta_len;
646 head->metadata.raw_size = g_wim_data.new_meta_len;
647
648 head->lookup.flags = 0;
649 head->lookup.offset = head->metadata.offset + g_wim_data.new_meta_align_len;
650 head->lookup.size_in_wim = g_wim_data.new_lookup_len;
651 head->lookup.raw_size = g_wim_data.new_lookup_len;
652
653 grub_file_close(file);
654
655 debug("%s", "windows locate wim finish\n");
656 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
657 }
658
659 static grub_uint32_t ventoy_get_override_chunk_num(void)
660 {
661 /* 1: block count in Partition Descriptor */
662 /* 2: file_size in file_entry or extend_file_entry */
663 /* 3: data_size and position in extend data short ad */
664 /* 4: new wim file header */
665 return 4;
666 }
667
668 static void ventoy_windows_fill_override_data( grub_uint64_t isosize, void *override)
669 {
670 grub_uint32_t data32;
671 grub_uint64_t data64;
672 grub_uint64_t sector;
673 grub_uint32_t new_wim_size;
674 ventoy_override_chunk *cur;
675
676 sector = (isosize + 2047) / 2048;
677
678 cur = (ventoy_override_chunk *)override;
679
680 new_wim_size = g_wim_data.wim_align_size + g_wim_data.bin_align_len +
681 g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len;
682
683 if (g_wim_data.iso_type == 0)
684 {
685 ventoy_iso9660_override *dirent = (ventoy_iso9660_override *)g_wim_data.override_data;
686
687 dirent->first_sector = (grub_uint32_t)sector;
688 dirent->size = new_wim_size;
689 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
690 dirent->size_be = grub_swap_bytes32(dirent->size);
691 }
692 else
693 {
694 ventoy_udf_override *udf = (ventoy_udf_override *)g_wim_data.override_data;
695 udf->length = new_wim_size;
696 udf->position = (grub_uint32_t)sector - g_wim_data.udf_start_block;
697 }
698
699 //override 1: sector number in pd data
700 cur->img_offset = grub_udf_get_last_pd_size_offset();
701 cur->override_size = 4;
702 data32 = sector - g_wim_data.udf_start_block + (new_wim_size / 2048);
703 grub_memcpy(cur->override_data, &(data32), 4);
704
705 //override 2: filesize in file_entry
706 cur++;
707 cur->img_offset = g_wim_data.fe_entry_size_offset;
708 cur->override_size = 8;
709 data64 = new_wim_size;
710 grub_memcpy(cur->override_data, &(data64), 8);
711
712 /* override 3: position and length in extend data */
713 cur++;
714 cur->img_offset = g_wim_data.override_offset;
715 cur->override_size = g_wim_data.override_len;
716 grub_memcpy(cur->override_data, g_wim_data.override_data, cur->override_size);
717
718 /* override 4: new wim file header */
719 cur++;
720 cur->img_offset = g_wim_data.file_offset;
721 cur->override_size = sizeof(wim_header);
722 grub_memcpy(cur->override_data, &(g_wim_data.wim_header), cur->override_size);
723
724 return;
725 }
726
727 static void ventoy_windows_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
728 {
729 grub_uint64_t sector;
730 grub_uint32_t offset;
731 grub_uint32_t wim_secs;
732 grub_uint32_t mem_secs;
733 char *override = NULL;
734 ventoy_virt_chunk *cur = NULL;
735
736 sector = (isosize + 2047) / 2048;
737 offset = sizeof(ventoy_virt_chunk);
738
739 wim_secs = g_wim_data.wim_align_size / 2048;
740 mem_secs = (g_wim_data.bin_align_len + g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len) / 2048;
741
742 override = (char *)chain + chain->virt_chunk_offset;
743 cur = (ventoy_virt_chunk *)override;
744
745 cur->remap_sector_start = sector;
746 cur->remap_sector_end = cur->remap_sector_start + wim_secs;
747 cur->org_sector_start = (grub_uint32_t)(g_wim_data.file_offset / 2048);
748
749 cur->mem_sector_start = cur->remap_sector_end;
750 cur->mem_sector_end = cur->mem_sector_start + mem_secs;
751 cur->mem_sector_offset = offset;
752
753 grub_memcpy(override + offset, g_wim_data.jump_bin_data, g_wim_data.bin_raw_len);
754 offset += g_wim_data.bin_align_len;
755
756 grub_memcpy(override + offset, g_wim_data.new_meta_data, g_wim_data.new_meta_len);
757 offset += g_wim_data.new_meta_align_len;
758
759 grub_memcpy(override + offset, g_wim_data.new_lookup_data, g_wim_data.new_lookup_len);
760 offset += g_wim_data.new_lookup_align_len;
761
762 chain->virt_img_size_in_bytes += g_wim_data.wim_align_size +
763 g_wim_data.bin_align_len +
764 g_wim_data.new_meta_align_len +
765 g_wim_data.new_lookup_align_len;
766 return;
767 }
768
769 static int ventoy_windows_drive_map(ventoy_chain_head *chain)
770 {
771 grub_disk_t disk;
772
773 debug("drive map begin <%p> ...\n", chain);
774
775 if (chain->disk_drive == 0x80)
776 {
777 disk = grub_disk_open("hd1");
778 if (disk)
779 {
780 grub_disk_close(disk);
781 debug("drive map needed %p\n", disk);
782 chain->drive_map = 0x81;
783 }
784 else
785 {
786 debug("failed to open disk %s\n", "hd1");
787 }
788 }
789 else
790 {
791 debug("no need to map 0x%x\n", chain->disk_drive);
792 }
793
794 return 0;
795 }
796
797 grub_err_t ventoy_cmd_windows_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
798 {
799 int unknown_image = 0;
800 int ventoy_compatible = 0;
801 grub_uint32_t size = 0;
802 grub_uint64_t isosize = 0;
803 grub_uint32_t boot_catlog = 0;
804 grub_uint32_t img_chunk_size = 0;
805 grub_uint32_t override_size = 0;
806 grub_uint32_t virt_chunk_size = 0;
807 grub_file_t file;
808 grub_disk_t disk;
809 const char *pLastChain = NULL;
810 const char *compatible;
811 ventoy_chain_head *chain;
812 char envbuf[64];
813
814 (void)ctxt;
815 (void)argc;
816
817 debug("chain data begin <%s> ...\n", args[0]);
818
819 compatible = grub_env_get("ventoy_compatible");
820 if (compatible && compatible[0] == 'Y')
821 {
822 ventoy_compatible = 1;
823 }
824
825 if (NULL == g_img_chunk_list.chunk)
826 {
827 grub_printf("ventoy not ready\n");
828 return 1;
829 }
830
831 if (0 == ventoy_compatible && g_wim_data.new_meta_data == NULL)
832 {
833 unknown_image = 1;
834 debug("Warning: %s was not recognized by Ventoy\n", args[0]);
835 }
836
837 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
838 if (!file)
839 {
840 return 1;
841 }
842
843 isosize = file->size;
844
845 boot_catlog = ventoy_get_iso_boot_catlog(file);
846 if (boot_catlog)
847 {
848 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
849 {
850 grub_env_set("LoadIsoEfiDriver", "on");
851 }
852 }
853 else
854 {
855 if (ventoy_is_efi_os())
856 {
857 grub_env_set("LoadIsoEfiDriver", "on");
858 }
859 else
860 {
861 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
862 }
863 }
864
865 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
866
867 if (ventoy_compatible || unknown_image)
868 {
869 size = sizeof(ventoy_chain_head) + img_chunk_size;
870 }
871 else
872 {
873 override_size = ventoy_get_override_chunk_num() * sizeof(ventoy_override_chunk);
874 virt_chunk_size = sizeof(ventoy_virt_chunk) + g_wim_data.bin_align_len +
875 g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len;;
876 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
877 }
878
879 pLastChain = grub_env_get("vtoy_chain_mem_addr");
880 if (pLastChain)
881 {
882 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
883 if (chain)
884 {
885 debug("free last chain memory %p\n", chain);
886 grub_free(chain);
887 }
888 }
889
890 chain = grub_malloc(size);
891 if (!chain)
892 {
893 grub_printf("Failed to alloc chain memory size %u\n", size);
894 grub_file_close(file);
895 return 1;
896 }
897
898 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
899 grub_env_set("vtoy_chain_mem_addr", envbuf);
900 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
901 grub_env_set("vtoy_chain_mem_size", envbuf);
902
903 grub_memset(chain, 0, sizeof(ventoy_chain_head));
904
905 /* part 1: os parameter */
906 ventoy_fill_os_param(file, &(chain->os_param));
907
908 if (g_wim_data.jump_bin_data && g_wim_data.new_meta_data)
909 {
910 ventoy_update_before_chain(&(chain->os_param), args[0]);
911 }
912
913 /* part 2: chain head */
914 disk = file->device->disk;
915 chain->disk_drive = disk->id;
916 chain->disk_sector_size = (1 << disk->log_sector_size);
917 chain->real_img_size_in_bytes = file->size;
918 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
919 chain->boot_catalog = boot_catlog;
920
921 if (!ventoy_is_efi_os())
922 {
923 grub_file_seek(file, boot_catlog * 2048);
924 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
925 }
926
927 /* part 3: image chunk */
928 chain->img_chunk_offset = sizeof(ventoy_chain_head);
929 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
930 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
931
932 if (ventoy_compatible || unknown_image)
933 {
934 return 0;
935 }
936
937 if (g_wim_data.new_meta_data == NULL)
938 {
939 return 0;
940 }
941
942 /* part 4: override chunk */
943 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
944 chain->override_chunk_num = ventoy_get_override_chunk_num();
945 ventoy_windows_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
946
947 /* part 5: virt chunk */
948 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
949 chain->virt_chunk_num = 1;
950 ventoy_windows_fill_virt_data(isosize, chain);
951
952 if (ventoy_is_efi_os() == 0)
953 {
954 ventoy_windows_drive_map(chain);
955 }
956
957 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
958 }
959
960