]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_unix.c
e38597ff5176e6c20c993f4c263b39d999758c2a
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / ventoy / ventoy_unix.c
1 /******************************************************************************
2 * ventoy_unix.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 #include <grub/types.h>
21 #include <grub/misc.h>
22 #include <grub/mm.h>
23 #include <grub/err.h>
24 #include <grub/dl.h>
25 #include <grub/disk.h>
26 #include <grub/device.h>
27 #include <grub/term.h>
28 #include <grub/partition.h>
29 #include <grub/file.h>
30 #include <grub/normal.h>
31 #include <grub/extcmd.h>
32 #include <grub/datetime.h>
33 #include <grub/i18n.h>
34 #include <grub/net.h>
35 #include <grub/time.h>
36 #include <grub/ventoy.h>
37 #include "ventoy_def.h"
38
39 GRUB_MOD_LICENSE ("GPLv3+");
40
41 char g_ko_mod_path[256];
42 int g_conf_new_len = 0;
43 char *g_conf_new_data = NULL;
44
45 int g_mod_new_len = 0;
46 char *g_mod_new_data = NULL;
47
48 grub_uint64_t g_mod_override_offset = 0;
49 grub_uint64_t g_conf_override_offset = 0;
50
51 static int ventoy_get_file_override(const char *filename, grub_uint64_t *offset)
52 {
53 grub_file_t file;
54
55 *offset = 0;
56
57 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(loop)%s", filename);
58 if (!file)
59 {
60 return 1;
61 }
62
63 *offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
64
65 grub_file_close(file);
66
67 return 0;
68 }
69
70 static grub_uint32_t ventoy_unix_get_override_chunk_count(void)
71 {
72 grub_uint32_t count = 0;
73
74 if (g_conf_new_len > 0)
75 {
76 count++;
77 }
78
79 if (g_mod_new_len > 0)
80 {
81 count++;
82 }
83
84 return count;
85 }
86
87 static grub_uint32_t ventoy_unix_get_virt_chunk_count(void)
88 {
89 grub_uint32_t count = 0;
90
91 if (g_conf_new_len > 0)
92 {
93 count++;
94 }
95
96 if (g_mod_new_len > 0)
97 {
98 count++;
99 }
100
101 return count;
102 }
103 static grub_uint32_t ventoy_unix_get_virt_chunk_size(void)
104 {
105 grub_uint32_t size;
106
107 size = sizeof(ventoy_virt_chunk) * ventoy_unix_get_virt_chunk_count();
108
109 if (g_conf_new_len > 0)
110 {
111 size += ventoy_align_2k(g_conf_new_len);
112 }
113
114 if (g_mod_new_len > 0)
115 {
116 size += ventoy_align_2k(g_mod_new_len);
117 }
118
119 return size;
120 }
121
122 static void ventoy_unix_fill_override_data( grub_uint64_t isosize, void *override)
123 {
124 grub_uint64_t sector;
125 ventoy_override_chunk *cur;
126 ventoy_iso9660_override *dirent;
127
128 sector = (isosize + 2047) / 2048;
129
130 cur = (ventoy_override_chunk *)override;
131
132 if (g_conf_new_len > 0)
133 {
134 /* loader.conf */
135 cur->img_offset = g_conf_override_offset;
136 cur->override_size = sizeof(ventoy_iso9660_override);
137 dirent = (ventoy_iso9660_override *)cur->override_data;
138 dirent->first_sector = (grub_uint32_t)sector;
139 dirent->size = (grub_uint32_t)g_conf_new_len;
140 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
141 dirent->size_be = grub_swap_bytes32(dirent->size);
142 sector += (dirent->size + 2047) / 2048;
143 }
144
145 if (g_mod_new_len > 0)
146 {
147 /* mod.ko */
148 cur++;
149 cur->img_offset = g_mod_override_offset;
150 cur->override_size = sizeof(ventoy_iso9660_override);
151 dirent = (ventoy_iso9660_override *)cur->override_data;
152 dirent->first_sector = (grub_uint32_t)sector;
153 dirent->size = (grub_uint32_t)g_mod_new_len;
154 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
155 dirent->size_be = grub_swap_bytes32(dirent->size);
156 sector += (dirent->size + 2047) / 2048;
157 }
158
159 return;
160 }
161
162 static void ventoy_unix_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
163 {
164 grub_uint64_t sector;
165 grub_uint32_t offset;
166 grub_uint32_t data_secs;
167 char *override;
168 ventoy_virt_chunk *cur;
169
170 override = (char *)chain + chain->virt_chunk_offset;
171 cur = (ventoy_virt_chunk *)override;
172
173 sector = (isosize + 2047) / 2048;
174 offset = 2 * sizeof(ventoy_virt_chunk);
175
176 if (g_conf_new_len > 0)
177 {
178 ventoy_unix_fill_virt(g_conf_new_data, g_conf_new_len);
179 }
180
181 if (g_mod_new_len > 0)
182 {
183 ventoy_unix_fill_virt(g_mod_new_data, g_mod_new_len);
184 }
185
186 return;
187 }
188
189 static int ventoy_freebsd_append_conf(char *buf, const char *isopath)
190 {
191 int pos = 0;
192 grub_uint32_t i;
193 grub_disk_t disk;
194 grub_file_t isofile;
195 char uuid[64] = {0};
196 ventoy_img_chunk *chunk;
197 grub_uint8_t disk_sig[4];
198 grub_uint8_t disk_guid[16];
199
200 debug("ventoy_freebsd_append_conf %s\n", isopath);
201
202 isofile = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", isopath);
203 if (!isofile)
204 {
205 return 1;
206 }
207
208 vtoy_ssprintf(buf, pos, "ventoy_load=\"%s\"\n", "YES");
209 vtoy_ssprintf(buf, pos, "ventoy_name=\"%s\"\n", g_ko_mod_path);
210
211 disk = isofile->device->disk;
212
213 ventoy_get_disk_guid(isofile->name, disk_guid, disk_sig);
214
215 for (i = 0; i < 16; i++)
216 {
217 grub_snprintf(uuid + i * 2, sizeof(uuid), "%02x", disk_guid[i]);
218 }
219
220 vtoy_ssprintf(buf, pos, "hint.ventoy.0.disksize=%llu\n", (ulonglong)(disk->total_sectors * (1 << disk->log_sector_size)));
221 vtoy_ssprintf(buf, pos, "hint.ventoy.0.diskuuid=\"%s\"\n", uuid);
222 vtoy_ssprintf(buf, pos, "hint.ventoy.0.disksignature=%02x%02x%02x%02x\n", disk_sig[0], disk_sig[1], disk_sig[2], disk_sig[3]);
223 vtoy_ssprintf(buf, pos, "hint.ventoy.0.segnum=%u\n", g_img_chunk_list.cur_chunk);
224
225 for (i = 0; i < g_img_chunk_list.cur_chunk; i++)
226 {
227 chunk = g_img_chunk_list.chunk + i;
228 vtoy_ssprintf(buf, pos, "hint.ventoy.%u.seg=\"0x%llx@0x%llx\"\n",
229 i, (ulonglong)(chunk->disk_start_sector * 512),
230 (ulonglong)((chunk->disk_end_sector + 1) * 512));
231 }
232
233 grub_file_close(isofile);
234
235 return pos;
236 }
237
238 grub_err_t ventoy_cmd_unix_reset(grub_extcmd_context_t ctxt, int argc, char **args)
239 {
240 (void)ctxt;
241 (void)argc;
242 (void)args;
243
244 g_conf_new_len = 0;
245 g_mod_new_len = 0;
246 g_mod_override_offset = 0;
247 g_conf_override_offset = 0;
248
249 check_free(g_mod_new_data, grub_free);
250 check_free(g_conf_new_data, grub_free);
251
252 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
253 }
254
255 grub_err_t ventoy_cmd_parse_freenas_ver(grub_extcmd_context_t ctxt, int argc, char **args)
256 {
257 grub_file_t file;
258 const char *ver = NULL;
259 char *buf = NULL;
260 VTOY_JSON *json = NULL;
261
262 (void)ctxt;
263 (void)argc;
264
265 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
266 if (!file)
267 {
268 debug("Failed to open file %s\n", args[0]);
269 return 1;
270 }
271
272 buf = grub_malloc(file->size + 2);
273 if (!buf)
274 {
275 grub_file_close(file);
276 return 0;
277 }
278 grub_file_read(file, buf, file->size);
279 buf[file->size] = 0;
280
281 json = vtoy_json_create();
282 if (!json)
283 {
284 goto end;
285 }
286
287 if (vtoy_json_parse(json, buf))
288 {
289 goto end;
290 }
291
292 ver = vtoy_json_get_string_ex(json->pstChild, "Version");
293 if (ver)
294 {
295 debug("freenas version:<%s>\n", ver);
296 ventoy_set_env(args[1], ver);
297 }
298 else
299 {
300 debug("freenas version:<%s>\n", "NOT FOUND");
301 grub_env_unset(args[1]);
302 }
303
304 end:
305 grub_check_free(buf);
306 check_free(json, vtoy_json_destroy);
307 grub_file_close(file);
308
309 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
310 }
311
312 grub_err_t ventoy_cmd_unix_freebsd_ver(grub_extcmd_context_t ctxt, int argc, char **args)
313 {
314 grub_file_t file;
315 char *buf;
316 char *start = NULL;
317 char *nextline = NULL;
318
319 (void)ctxt;
320 (void)argc;
321 (void)args;
322
323 file = ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD, "%s", args[0]);
324 if (!file)
325 {
326 debug("Failed to open file %s\n", args[0]);
327 return 1;
328 }
329
330 buf = grub_zalloc(file->size + 2);
331 if (!buf)
332 {
333 grub_file_close(file);
334 return 0;
335 }
336 grub_file_read(file, buf, file->size);
337
338 for (start = buf; start; start = nextline)
339 {
340 if (grub_strncmp(start, "USERLAND_VERSION", 16) == 0)
341 {
342 nextline = start;
343 while (*nextline && *nextline != '\r' && *nextline != '\n')
344 {
345 nextline++;
346 }
347
348 *nextline = 0;
349 break;
350 }
351 nextline = ventoy_get_line(start);
352 }
353
354 if (start)
355 {
356 debug("freebsd version:<%s>\n", start);
357 ventoy_set_env(args[1], start);
358 }
359 else
360 {
361 debug("freebsd version:<%s>\n", "NOT FOUND");
362 grub_env_unset(args[1]);
363 }
364
365 grub_free(buf);
366 grub_file_close(file);
367
368 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
369 }
370
371 grub_err_t ventoy_cmd_unix_replace_conf(grub_extcmd_context_t ctxt, int argc, char **args)
372 {
373 grub_uint32_t i;
374 char *data;
375 grub_uint64_t offset;
376 grub_file_t file;
377 const char *confile = NULL;
378 const char * loader_conf[] =
379 {
380 "/boot/loader.conf",
381 "/boot/defaults/loader.conf",
382 };
383
384 (void)ctxt;
385
386 if (argc != 2)
387 {
388 debug("Replace conf invalid argc %d\n", argc);
389 return 1;
390 }
391
392 for (i = 0; i < sizeof(loader_conf) / sizeof(loader_conf[0]); i++)
393 {
394 if (ventoy_get_file_override(loader_conf[i], &offset) == 0)
395 {
396 confile = loader_conf[i];
397 g_conf_override_offset = offset;
398 break;
399 }
400 }
401
402 if (confile == NULL)
403 {
404 debug("Can't find loader.conf file from %u locations\n", i);
405 return 1;
406 }
407
408 file = ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD, "(loop)/%s", confile);
409 if (!file)
410 {
411 debug("Failed to open %s \n", confile);
412 return 1;
413 }
414
415 debug("old conf file size:%d\n", (int)file->size);
416
417 data = grub_malloc(VTOY_MAX_SCRIPT_BUF);
418 if (!data)
419 {
420 grub_file_close(file);
421 return 1;
422 }
423
424 grub_file_read(file, data, file->size);
425 grub_file_close(file);
426
427 g_conf_new_data = data;
428 g_conf_new_len = (int)file->size;
429
430 if (grub_strcmp(args[0], "FreeBSD") == 0)
431 {
432 g_conf_new_len += ventoy_freebsd_append_conf(data + file->size, args[1]);
433 }
434
435 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
436 }
437
438 grub_err_t ventoy_cmd_unix_replace_ko(grub_extcmd_context_t ctxt, int argc, char **args)
439 {
440 char *data;
441 grub_uint64_t offset;
442 grub_file_t file;
443
444 (void)ctxt;
445
446 if (argc != 2)
447 {
448 debug("Replace ko invalid argc %d\n", argc);
449 return 1;
450 }
451
452 debug("replace ko %s\n", args[0]);
453
454 if (ventoy_get_file_override(args[0], &offset) == 0)
455 {
456 grub_snprintf(g_ko_mod_path, sizeof(g_ko_mod_path), "%s", args[0]);
457 g_mod_override_offset = offset;
458 }
459 else
460 {
461 debug("Can't find replace ko file from %s\n", args[0]);
462 return 1;
463 }
464
465 file = ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD, "%s", args[1]);
466 if (!file)
467 {
468 debug("Failed to open %s \n", args[1]);
469 return 1;
470 }
471
472 debug("new ko file size:%d\n", (int)file->size);
473
474 data = grub_malloc(file->size);
475 if (!data)
476 {
477 grub_file_close(file);
478 return 1;
479 }
480
481 grub_file_read(file, data, file->size);
482 grub_file_close(file);
483
484 g_mod_new_data = data;
485 g_mod_new_len = (int)file->size;
486
487 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
488 }
489
490 grub_err_t ventoy_cmd_unix_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
491 {
492 int ventoy_compatible = 0;
493 grub_uint32_t size = 0;
494 grub_uint64_t isosize = 0;
495 grub_uint32_t boot_catlog = 0;
496 grub_uint32_t img_chunk_size = 0;
497 grub_uint32_t override_count = 0;
498 grub_uint32_t override_size = 0;
499 grub_uint32_t virt_chunk_size = 0;
500 grub_file_t file;
501 grub_disk_t disk;
502 const char *pLastChain = NULL;
503 const char *compatible;
504 ventoy_chain_head *chain;
505 char envbuf[64];
506
507 (void)ctxt;
508 (void)argc;
509
510 compatible = grub_env_get("ventoy_compatible");
511 if (compatible && compatible[0] == 'Y')
512 {
513 ventoy_compatible = 1;
514 }
515
516 if (NULL == g_img_chunk_list.chunk)
517 {
518 grub_printf("ventoy not ready\n");
519 return 1;
520 }
521
522 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
523 if (!file)
524 {
525 return 1;
526 }
527
528 isosize = file->size;
529
530 boot_catlog = ventoy_get_iso_boot_catlog(file);
531 if (boot_catlog)
532 {
533 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
534 {
535 grub_env_set("LoadIsoEfiDriver", "on");
536 }
537 }
538 else
539 {
540 if (ventoy_is_efi_os())
541 {
542 grub_env_set("LoadIsoEfiDriver", "on");
543 }
544 else
545 {
546 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
547 }
548 }
549
550 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
551
552 if (ventoy_compatible)
553 {
554 size = sizeof(ventoy_chain_head) + img_chunk_size;
555 }
556 else
557 {
558 override_count = ventoy_unix_get_override_chunk_count();
559 override_size = override_count * sizeof(ventoy_override_chunk);
560
561 virt_chunk_size = ventoy_unix_get_virt_chunk_size();
562 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
563 }
564
565 pLastChain = grub_env_get("vtoy_chain_mem_addr");
566 if (pLastChain)
567 {
568 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
569 if (chain)
570 {
571 debug("free last chain memory %p\n", chain);
572 grub_free(chain);
573 }
574 }
575
576 chain = grub_malloc(size);
577 if (!chain)
578 {
579 grub_printf("Failed to alloc chain memory size %u\n", size);
580 grub_file_close(file);
581 return 1;
582 }
583
584 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
585 grub_env_set("vtoy_chain_mem_addr", envbuf);
586 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
587 grub_env_set("vtoy_chain_mem_size", envbuf);
588
589 grub_memset(chain, 0, sizeof(ventoy_chain_head));
590
591 /* part 1: os parameter */
592 g_ventoy_chain_type = ventoy_chain_linux;
593 ventoy_fill_os_param(file, &(chain->os_param));
594
595 /* part 2: chain head */
596 disk = file->device->disk;
597 chain->disk_drive = disk->id;
598 chain->disk_sector_size = (1 << disk->log_sector_size);
599 chain->real_img_size_in_bytes = file->size;
600 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
601 chain->boot_catalog = boot_catlog;
602
603 if (!ventoy_is_efi_os())
604 {
605 grub_file_seek(file, boot_catlog * 2048);
606 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
607 }
608
609 /* part 3: image chunk */
610 chain->img_chunk_offset = sizeof(ventoy_chain_head);
611 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
612 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
613
614 if (ventoy_compatible)
615 {
616 return 0;
617 }
618
619 /* part 4: override chunk */
620 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
621 chain->override_chunk_num = override_count;
622 ventoy_unix_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
623
624 /* part 5: virt chunk */
625 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
626 chain->virt_chunk_num = ventoy_unix_get_virt_chunk_count();
627 ventoy_unix_fill_virt_data(isosize, chain);
628
629 grub_file_close(file);
630
631 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
632 }
633