]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_browser.c
6b5a10e379660ba5c4283ef2cf03c77bb503ac17
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / ventoy / ventoy_browser.c
1 /******************************************************************************
2 * ventoy_browser.c
3 *
4 * Copyright (c) 2022, 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 #define BROWSER_MENU_BUF 65536
42
43 static const char *g_vtoy_dev = NULL;
44 static grub_fs_t g_menu_fs = NULL;
45 static char *g_menu_device = NULL;
46 static grub_device_t g_menu_dev = NULL;
47 static char g_menu_path_buf[1024];
48 static int g_menu_path_len = 0;
49 static browser_node *g_browser_list = NULL;
50
51 static int ventoy_browser_strcmp(char *str1, char *str2)
52 {
53 char *s1, *s2;
54 int c1 = 0;
55 int c2 = 0;
56
57 for (s1 = str1, s2 = str2; *s1 && *s2; s1++, s2++)
58 {
59 c1 = *s1;
60 c2 = *s2;
61
62 if (0 == g_sort_case_sensitive)
63 {
64 if (grub_islower(c1))
65 {
66 c1 = c1 - 'a' + 'A';
67 }
68
69 if (grub_islower(c2))
70 {
71 c2 = c2 - 'a' + 'A';
72 }
73 }
74
75 if (c1 != c2)
76 {
77 break;
78 }
79 }
80
81 return (c1 - c2);
82 }
83
84 static int ventoy_browser_mbuf_alloc(browser_mbuf *mbuf)
85 {
86 grub_memset(mbuf, 0, sizeof(browser_mbuf));
87 mbuf->buf = grub_malloc(BROWSER_MENU_BUF);
88 if (!mbuf->buf)
89 {
90 return 0;
91 }
92
93 mbuf->pos = 0;
94 mbuf->max = BROWSER_MENU_BUF;
95 return 1;
96 }
97
98 static inline void ventoy_browser_mbuf_free(browser_mbuf *mbuf)
99 {
100 if (mbuf)
101 grub_check_free(mbuf->buf)
102 }
103
104 static inline int ventoy_browser_mbuf_extend(browser_mbuf *mbuf)
105 {
106 if (mbuf->max - mbuf->pos <= VTOY_SIZE_1KB)
107 {
108 mbuf->max += BROWSER_MENU_BUF;
109 mbuf->buf = grub_realloc(mbuf->buf, mbuf->max);
110 }
111
112 return 0;
113 }
114
115 static browser_node * ventoy_browser_find_top_node(int dir)
116 {
117 browser_node *node = NULL;
118 browser_node *sel = NULL;
119
120 for (node = g_browser_list; node; node = node->next)
121 {
122 if (node->dir == dir)
123 {
124 if (sel)
125 {
126 if (ventoy_browser_strcmp(sel->filename, node->filename) > 0)
127 {
128 sel = node;
129 }
130 }
131 else
132 {
133 sel = node;
134 }
135 }
136 }
137
138 return sel;
139 }
140
141 static int ventoy_browser_iterate_partition(struct grub_disk *disk, const grub_partition_t partition, void *data)
142 {
143 char partname[64];
144 char title[256];
145 grub_device_t dev;
146 grub_fs_t fs;
147 char *Label = NULL;
148 browser_mbuf *mbuf = (browser_mbuf *)data;
149
150 (void)data;
151
152 if (partition->number == 1 && g_vtoy_dev && grub_strcmp(disk->name, g_vtoy_dev) == 0)
153 {
154 return 0;
155 }
156
157 grub_snprintf(partname, sizeof(partname) - 1, "%s,%d", disk->name, partition->number + 1);
158
159 dev = grub_device_open(partname);
160 if (!dev)
161 {
162 return 0;
163 }
164
165 fs = grub_fs_probe(dev);
166 if (!fs)
167 {
168 grub_device_close(dev);
169 return 0;
170 }
171
172 fs->fs_label(dev, &Label);
173
174 if (ventoy_check_file_exist("(%s)/.ventoyignore", partname))
175 {
176 return 0;
177 }
178
179 if (g_tree_view_menu_style == 0)
180 {
181 grub_snprintf(title, sizeof(title), "%-10s (%s,%s%d) [%s] %s %s",
182 "DISK", disk->name, partition->msdostype == 0xee ? "gpt" : "msdos",
183 partition->number + 1, (Label ? Label : ""), fs->name,
184 grub_get_human_size(partition->len << disk->log_sector_size, GRUB_HUMAN_SIZE_SHORT));
185 }
186 else
187 {
188 grub_snprintf(title, sizeof(title), "(%s,%s%d) [%s] %s %s",
189 disk->name, partition->msdostype == 0xee ? "gpt" : "msdos",
190 partition->number + 1, (Label ? Label : ""), fs->name,
191 grub_get_human_size(partition->len << disk->log_sector_size, GRUB_HUMAN_SIZE_SHORT));
192 }
193
194 if (ventoy_get_fs_type(fs->name) >= ventoy_fs_max)
195 {
196 browser_ssprintf(mbuf, "menuentry \"%s\" --class=vtoydisk {\n"
197 " echo \"unsupported file system type!\" \n"
198 " ventoy_pause\n"
199 "}\n",
200 title);
201 }
202 else
203 {
204 browser_ssprintf(mbuf, "menuentry \"%s\" --class=vtoydisk {\n"
205 " vt_browser_dir %s,%d 0x%lx /\n"
206 "}\n",
207 title, disk->name, partition->number + 1, (ulong)fs);
208 }
209
210 ventoy_browser_mbuf_extend(mbuf);
211
212 return 0;
213 }
214
215 static int ventoy_browser_iterate_disk(const char *name, void *data)
216 {
217 grub_disk_t disk;
218
219 if (name[0] != 'h')
220 {
221 return 0;
222 }
223
224 disk = grub_disk_open(name);
225 if (disk)
226 {
227 grub_partition_iterate(disk, ventoy_browser_iterate_partition, data);
228 grub_disk_close(disk);
229 }
230
231 return 0;
232 }
233
234 static int ventoy_browser_valid_dirname(const char *name, int len)
235 {
236 if ((len == 1 && name[0] == '.') ||
237 (len == 2 && name[0] == '.' && name[1] == '.'))
238 {
239 return 0;
240 }
241
242 if (!ventoy_img_name_valid(name, len))
243 {
244 return 0;
245 }
246
247 if (name[0] == '$')
248 {
249 if (0 == grub_strncmp(name, "$RECYCLE.BIN", 12) ||
250 0 == grub_strncasecmp(name, "$Extend", 7))
251 {
252 return 0;
253 }
254 }
255
256 if (len == 25 && grub_strncmp(name, "System Volume Information", 25) == 0)
257 {
258 return 0;
259 }
260
261 return 1;
262 }
263
264 static int ventoy_browser_valid_filename(const char *filename, int len, int *type)
265 {
266 if (len < 4)
267 {
268 return 0;
269 }
270
271 if (FILE_FLT(ISO) && 0 == grub_strcasecmp(filename + len - 4, ".iso"))
272 {
273 *type = img_type_iso;
274 }
275 else if (FILE_FLT(WIM) && g_wimboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".wim")))
276 {
277 *type = img_type_wim;
278 }
279 else if (FILE_FLT(VHD) && g_vhdboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".vhd") ||
280 (len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vhdx"))))
281 {
282 *type = img_type_vhd;
283 }
284 #ifdef GRUB_MACHINE_EFI
285 else if (FILE_FLT(EFI) && 0 == grub_strcasecmp(filename + len - 4, ".efi"))
286 {
287 *type = img_type_efi;
288 }
289 #endif
290 else if (FILE_FLT(IMG) && 0 == grub_strcasecmp(filename + len - 4, ".img"))
291 {
292 if (len == 18 && grub_strncmp(filename, "ventoy_", 7) == 0)
293 {
294 if (grub_strncmp(filename + 7, "wimboot", 7) == 0 ||
295 grub_strncmp(filename + 7, "vhdboot", 7) == 0)
296 {
297 return 0;
298 }
299 }
300 *type = img_type_img;
301 }
302 else if (FILE_FLT(VTOY) && len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vtoy"))
303 {
304 *type = img_type_vtoy;
305 }
306 else
307 {
308 return 0;
309 }
310
311 if (g_filt_dot_underscore_file && filename[0] == '.' && filename[1] == '_')
312 {
313 return 0;
314 }
315
316 return 1;
317 }
318
319 static int ventoy_browser_check_ignore(const char *device, const char *root, const char *dir)
320 {
321 grub_file_t file;
322 char fullpath[1024] = {0};
323
324 grub_snprintf(fullpath, 1023, "(%s)%s/%s/.ventoyignore", device, root, dir);
325 file = grub_file_open(fullpath, GRUB_FILE_TYPE_NONE);
326 if (!file)
327 {
328 grub_errno = 0;
329 return 0;
330 }
331 else
332 {
333 grub_file_close(file);
334 return 1;
335 }
336 }
337
338 static int ventoy_browser_iterate_dir(const char *filename, const struct grub_dirhook_info *info, void *data)
339 {
340 int type;
341 int len;
342 browser_node *node;
343
344 (void)data;
345
346 len = grub_strlen(filename);
347
348 if (info->dir)
349 {
350 if (!ventoy_browser_valid_dirname(filename, len))
351 {
352 return 0;
353 }
354
355 if (ventoy_browser_check_ignore(g_menu_device, g_menu_path_buf, filename))
356 {
357 return 0;
358 }
359
360 node = grub_zalloc(sizeof(browser_node));
361 if (!node)
362 {
363 return 0;
364 }
365
366 node->dir = 1;
367 grub_strncpy(node->filename, filename, sizeof(node->filename));
368
369 if (g_tree_view_menu_style == 0)
370 {
371 grub_snprintf(node->menuentry, sizeof(node->menuentry),
372 "menuentry \"%-10s [%s]\" --class=vtoydir {\n"
373 " vt_browser_dir %s 0x%lx \"%s/%s\"\n"
374 "}\n",
375 "DIR", filename, g_menu_device, (ulong)g_menu_fs, g_menu_path_buf, filename);
376 }
377 else
378 {
379 grub_snprintf(node->menuentry, sizeof(node->menuentry),
380 "menuentry \"[%s]\" --class=vtoydir {\n"
381 " vt_browser_dir %s 0x%lx \"%s/%s\"\n"
382 "}\n",
383 filename, g_menu_device, (ulong)g_menu_fs, g_menu_path_buf, filename);
384 }
385 }
386 else
387 {
388 grub_uint64_t fsize = info->size;
389
390 if (!ventoy_browser_valid_filename(filename, len, &type))
391 {
392 return 0;
393 }
394
395 if (grub_file_is_vlnk_suffix(filename, len))
396 {
397 return 0;
398 }
399
400 node = grub_zalloc(sizeof(browser_node));
401 if (!node)
402 {
403 return 0;
404 }
405
406 if (fsize == 0)
407 {
408 struct grub_file file;
409
410 grub_memset(&file, 0, sizeof(file));
411 file.device = g_menu_dev;
412 grub_snprintf(node->menuentry, sizeof(node->menuentry), "%s/%s", g_menu_path_buf, filename);
413 if (g_menu_fs->fs_open(&file, node->menuentry) == GRUB_ERR_NONE)
414 {
415 fsize = file.size;
416 g_menu_fs->fs_close(&file);
417 }
418 }
419
420 node->dir = 0;
421 grub_strncpy(node->filename, filename, sizeof(node->filename));
422
423 if (g_tree_view_menu_style == 0)
424 {
425 grub_snprintf(node->menuentry, sizeof(node->menuentry),
426 "menuentry \"%-10s %s\" --class=%s {\n"
427 " vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
428 " %s_common_menuentry\n"
429 " vt_reset_fake_vlnk\n"
430 "}\n",
431 grub_get_human_size(fsize, GRUB_HUMAN_SIZE_SHORT), filename, g_menu_class[type],
432 g_menu_device, g_menu_path_buf, filename, g_menu_prefix[type], (ulonglong)fsize,
433 g_menu_prefix[type]);
434 }
435 else
436 {
437 grub_snprintf(node->menuentry, sizeof(node->menuentry),
438 "menuentry \"%s\" --class=%s {\n"
439 " vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
440 " %s_common_menuentry\n"
441 " vt_reset_fake_vlnk\n"
442 "}\n",
443 filename, g_menu_class[type],
444 g_menu_device, g_menu_path_buf, filename, g_menu_prefix[type], (ulonglong)fsize,
445 g_menu_prefix[type]);
446 }
447 }
448
449 node->prev = NULL;
450 node->next = g_browser_list;
451 if (g_browser_list)
452 {
453 g_browser_list->prev = node;
454 }
455 g_browser_list = node;
456
457 return 0;
458 }
459
460 static grub_err_t ventoy_browser_iso_part(void)
461 {
462 char cfgfile[64];
463 char *buffer = NULL;
464 int pos = 0;
465 int buflen = 0;
466 int cfglen = 0;
467
468 cfglen = g_tree_script_pos - g_tree_script_pre;
469 buflen = cfglen + 512;
470 buffer = grub_malloc(buflen);
471 if (!buffer)
472 {
473 return 1;
474 }
475
476 if (g_tree_view_menu_style == 0)
477 {
478 pos = grub_snprintf(buffer, buflen, "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n "
479 " echo 'return ...' \n}\n", "<--");
480 }
481 else
482 {
483 pos = grub_snprintf(buffer, buflen, "menuentry \"[../]\" --class=\"vtoyret\" VTOY_RET {\n "
484 " echo 'return ...' \n}\n");
485 }
486
487 grub_memcpy(buffer + pos, g_tree_script_buf + g_tree_script_pre, cfglen);
488 pos += cfglen;
489
490 grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)buffer, pos);
491 grub_script_execute_sourcecode(cfgfile);
492
493 grub_free(buffer);
494 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
495 }
496
497 grub_err_t ventoy_cmd_browser_dir(grub_extcmd_context_t ctxt, int argc, char **args)
498 {
499 int i;
500 grub_fs_t fs;
501 grub_device_t dev;
502 char cfgfile[64];
503 browser_node *node;
504 browser_mbuf mbuf;
505
506 (void)ctxt;
507 (void)argc;
508
509 if (args[2][0] == '/' && args[2][1] == 0)
510 {
511 grub_snprintf(cfgfile, sizeof(cfgfile), "(%s)", args[0]);
512 if (grub_strcmp(cfgfile, g_iso_path) == 0)
513 {
514 return ventoy_browser_iso_part();
515 }
516 }
517
518 if (!ventoy_browser_mbuf_alloc(&mbuf))
519 {
520 return 1;
521 }
522
523 fs = (grub_fs_t)grub_strtoul(args[1], NULL, 16);
524 if (!fs)
525 {
526 debug("Invalid fs %s\n", args[1]);
527 return 1;
528 }
529
530 dev = grub_device_open(args[0]);
531 if (!dev)
532 {
533 debug("Failed to open device %s\n", args[0]);
534 return 1;
535 }
536
537 g_menu_fs = fs;
538 g_menu_device = args[0];
539 g_menu_dev = dev;
540 g_browser_list = NULL;
541
542 if (args[2][0] == '/' && args[2][1] == 0)
543 {
544 g_menu_path_len = 0;
545 g_menu_path_buf[0] = 0;
546 fs->fs_dir(dev, "/", ventoy_browser_iterate_dir, NULL);
547 }
548 else
549 {
550 g_menu_path_len = grub_snprintf(g_menu_path_buf, sizeof(g_menu_path_buf), "%s", args[2]);
551 fs->fs_dir(dev, g_menu_path_buf, ventoy_browser_iterate_dir, NULL);
552 }
553 grub_device_close(dev);
554
555 if (g_tree_view_menu_style == 0)
556 {
557 browser_ssprintf(&mbuf, "menuentry \"%-10s [(%s)%s/..]\" --class=\"vtoyret\" VTOY_RET {\n "
558 " echo 'return ...' \n}\n", "<--", args[0], g_menu_path_buf);
559 }
560 else
561 {
562 browser_ssprintf(&mbuf, "menuentry \"[(%s)%s/..]\" --class=\"vtoyret\" VTOY_RET {\n "
563 " echo 'return ...' \n}\n", args[0], g_menu_path_buf);
564 }
565
566 for (i = 1; i >= 0; i--)
567 {
568 while (1)
569 {
570 node = ventoy_browser_find_top_node(i);
571 if (node)
572 {
573 browser_ssprintf(&mbuf, "%s", node->menuentry);
574 ventoy_browser_mbuf_extend(&mbuf);
575
576 if (node->prev)
577 {
578 node->prev->next = node->next;
579 }
580 if (node->next)
581 {
582 node->next->prev = node->prev;
583 }
584
585 if (node == g_browser_list)
586 {
587 g_browser_list = node->next;
588 }
589 grub_free(node);
590 }
591 else
592 {
593 break;
594 }
595 }
596 }
597 g_browser_list = NULL;
598
599 grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)mbuf.buf, mbuf.pos);
600 grub_script_execute_sourcecode(cfgfile);
601
602 ventoy_browser_mbuf_free(&mbuf);
603 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
604 }
605
606 grub_err_t ventoy_cmd_browser_disk(grub_extcmd_context_t ctxt, int argc, char **args)
607 {
608 char cfgfile[64];
609 browser_mbuf mbuf;
610
611 (void)ctxt;
612 (void)argc;
613 (void)args;
614
615 if (!ventoy_browser_mbuf_alloc(&mbuf))
616 {
617 return 1;
618 }
619
620 g_vtoy_dev = grub_env_get("vtoydev");
621
622 if (g_tree_view_menu_style == 0)
623 {
624 browser_ssprintf(&mbuf, "menuentry \"%-10s [%s]\" --class=\"vtoyret\" VTOY_RET {\n "
625 " echo 'return ...' \n}\n", "<--",
626 ventoy_get_vmenu_title("VTLANG_BROWER_RETURN"));
627 }
628 else
629 {
630 browser_ssprintf(&mbuf, "menuentry \"[%s]\" --class=\"vtoyret\" VTOY_RET {\n "
631 " echo 'return ...' \n}\n",
632 ventoy_get_vmenu_title("VTLANG_BROWER_RETURN"));
633 }
634
635 grub_disk_dev_iterate(ventoy_browser_iterate_disk, &mbuf);
636
637 grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)mbuf.buf, mbuf.pos);
638 grub_script_execute_sourcecode(cfgfile);
639
640 ventoy_browser_mbuf_free(&mbuf);
641 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
642 }
643