1 /******************************************************************************
4 * Copyright (c) 2022, longpanda <admin@ventoy.net>
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.
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.
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/>.
20 #include <grub/types.h>
21 #include <grub/misc.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>
35 #include <grub/time.h>
36 #include <grub/ventoy.h>
37 #include "ventoy_def.h"
39 GRUB_MOD_LICENSE ("GPLv3+");
41 #define BROWSER_MENU_BUF 65536
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
;
51 static int ventoy_browser_strcmp(char *str1
, char *str2
)
57 for (s1
= str1
, s2
= str2
; *s1
&& *s2
; s1
++, s2
++)
62 if (0 == g_sort_case_sensitive
)
84 static int ventoy_browser_mbuf_alloc(browser_mbuf
*mbuf
)
86 grub_memset(mbuf
, 0, sizeof(browser_mbuf
));
87 mbuf
->buf
= grub_malloc(BROWSER_MENU_BUF
);
94 mbuf
->max
= BROWSER_MENU_BUF
;
98 static inline void ventoy_browser_mbuf_free(browser_mbuf
*mbuf
)
101 grub_check_free(mbuf
->buf
)
104 static inline int ventoy_browser_mbuf_extend(browser_mbuf
*mbuf
)
106 if (mbuf
->max
- mbuf
->pos
<= VTOY_SIZE_1KB
)
108 mbuf
->max
+= BROWSER_MENU_BUF
;
109 mbuf
->buf
= grub_realloc(mbuf
->buf
, mbuf
->max
);
115 static browser_node
* ventoy_browser_find_top_node(int dir
)
117 browser_node
*node
= NULL
;
118 browser_node
*sel
= NULL
;
120 for (node
= g_browser_list
; node
; node
= node
->next
)
122 if (node
->dir
== dir
)
126 if (ventoy_browser_strcmp(sel
->filename
, node
->filename
) > 0)
141 static int ventoy_browser_iterate_partition(struct grub_disk
*disk
, const grub_partition_t partition
, void *data
)
148 browser_mbuf
*mbuf
= (browser_mbuf
*)data
;
152 if (partition
->number
== 1 && g_vtoy_dev
&& grub_strcmp(disk
->name
, g_vtoy_dev
) == 0)
157 grub_snprintf(partname
, sizeof(partname
) - 1, "%s,%d", disk
->name
, partition
->number
+ 1);
159 dev
= grub_device_open(partname
);
165 fs
= grub_fs_probe(dev
);
168 grub_device_close(dev
);
172 fs
->fs_label(dev
, &Label
);
174 if (ventoy_check_file_exist("(%s)/.ventoyignore", partname
))
179 if (g_tree_view_menu_style
== 0)
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
));
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
));
194 if (ventoy_get_fs_type(fs
->name
) >= ventoy_fs_max
)
196 browser_ssprintf(mbuf
, "menuentry \"%s\" --class=vtoydisk {\n"
197 " echo \"unsupported file system type!\" \n"
204 browser_ssprintf(mbuf
, "menuentry \"%s\" --class=vtoydisk {\n"
205 " vt_browser_dir %s,%d 0x%lx /\n"
207 title
, disk
->name
, partition
->number
+ 1, (ulong
)fs
);
210 ventoy_browser_mbuf_extend(mbuf
);
215 static int ventoy_browser_iterate_disk(const char *name
, void *data
)
224 disk
= grub_disk_open(name
);
227 grub_partition_iterate(disk
, ventoy_browser_iterate_partition
, data
);
228 grub_disk_close(disk
);
234 static int ventoy_browser_valid_dirname(const char *name
, int len
)
236 if ((len
== 1 && name
[0] == '.') ||
237 (len
== 2 && name
[0] == '.' && name
[1] == '.'))
242 if (!ventoy_img_name_valid(name
, len
))
249 if (0 == grub_strncmp(name
, "$RECYCLE.BIN", 12) ||
250 0 == grub_strncasecmp(name
, "$Extend", 7))
256 if (len
== 25 && grub_strncmp(name
, "System Volume Information", 25) == 0)
264 static int ventoy_browser_valid_filename(const char *filename
, int len
, int *type
)
271 if (FILE_FLT(ISO
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".iso"))
273 *type
= img_type_iso
;
275 else if (FILE_FLT(WIM
) && g_wimboot_enable
&& (0 == grub_strcasecmp(filename
+ len
- 4, ".wim")))
277 *type
= img_type_wim
;
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"))))
282 *type
= img_type_vhd
;
284 #ifdef GRUB_MACHINE_EFI
285 else if (FILE_FLT(EFI
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".efi"))
287 *type
= img_type_efi
;
290 else if (FILE_FLT(IMG
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".img"))
292 if (len
== 18 && grub_strncmp(filename
, "ventoy_", 7) == 0)
294 if (grub_strncmp(filename
+ 7, "wimboot", 7) == 0 ||
295 grub_strncmp(filename
+ 7, "vhdboot", 7) == 0)
300 *type
= img_type_img
;
302 else if (FILE_FLT(VTOY
) && len
>= 5 && 0 == grub_strcasecmp(filename
+ len
- 5, ".vtoy"))
304 *type
= img_type_vtoy
;
311 if (g_filt_dot_underscore_file
&& filename
[0] == '.' && filename
[1] == '_')
319 static int ventoy_browser_check_ignore(const char *device
, const char *root
, const char *dir
)
322 char fullpath
[1024] = {0};
324 grub_snprintf(fullpath
, 1023, "(%s)%s/%s/.ventoyignore", device
, root
, dir
);
325 file
= grub_file_open(fullpath
, GRUB_FILE_TYPE_NONE
);
333 grub_file_close(file
);
338 static int ventoy_browser_iterate_dir(const char *filename
, const struct grub_dirhook_info
*info
, void *data
)
346 len
= grub_strlen(filename
);
350 if (!ventoy_browser_valid_dirname(filename
, len
))
355 if (ventoy_browser_check_ignore(g_menu_device
, g_menu_path_buf
, filename
))
360 node
= grub_zalloc(sizeof(browser_node
));
367 grub_strncpy(node
->filename
, filename
, sizeof(node
->filename
));
369 if (g_tree_view_menu_style
== 0)
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"
375 "DIR", filename
, g_menu_device
, (ulong
)g_menu_fs
, g_menu_path_buf
, filename
);
379 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
380 "menuentry \"[%s]\" --class=vtoydir {\n"
381 " vt_browser_dir %s 0x%lx \"%s/%s\"\n"
383 filename
, g_menu_device
, (ulong
)g_menu_fs
, g_menu_path_buf
, filename
);
388 grub_uint64_t fsize
= info
->size
;
390 if (!ventoy_browser_valid_filename(filename
, len
, &type
))
395 if (grub_file_is_vlnk_suffix(filename
, len
))
400 node
= grub_zalloc(sizeof(browser_node
));
408 struct grub_file file
;
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
)
416 g_menu_fs
->fs_close(&file
);
421 grub_strncpy(node
->filename
, filename
, sizeof(node
->filename
));
423 if (g_tree_view_menu_style
== 0)
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"
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
]);
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"
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
]);
450 node
->next
= g_browser_list
;
453 g_browser_list
->prev
= node
;
455 g_browser_list
= node
;
460 static grub_err_t
ventoy_browser_iso_part(void)
468 cfglen
= g_tree_script_pos
- g_tree_script_pre
;
469 buflen
= cfglen
+ 512;
470 buffer
= grub_malloc(buflen
);
476 if (g_tree_view_menu_style
== 0)
478 pos
= grub_snprintf(buffer
, buflen
, "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n "
479 " echo 'return ...' \n}\n", "<--");
483 pos
= grub_snprintf(buffer
, buflen
, "menuentry \"[../]\" --class=\"vtoyret\" VTOY_RET {\n "
484 " echo 'return ...' \n}\n");
487 grub_memcpy(buffer
+ pos
, g_tree_script_buf
+ g_tree_script_pre
, cfglen
);
490 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)buffer
, pos
);
491 grub_script_execute_sourcecode(cfgfile
);
494 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
497 grub_err_t
ventoy_cmd_browser_dir(grub_extcmd_context_t ctxt
, int argc
, char **args
)
509 if (args
[2][0] == '/' && args
[2][1] == 0)
511 grub_snprintf(cfgfile
, sizeof(cfgfile
), "(%s)", args
[0]);
512 if (grub_strcmp(cfgfile
, g_iso_path
) == 0)
514 return ventoy_browser_iso_part();
518 if (!ventoy_browser_mbuf_alloc(&mbuf
))
523 fs
= (grub_fs_t
)grub_strtoul(args
[1], NULL
, 16);
526 debug("Invalid fs %s\n", args
[1]);
530 dev
= grub_device_open(args
[0]);
533 debug("Failed to open device %s\n", args
[0]);
538 g_menu_device
= args
[0];
540 g_browser_list
= NULL
;
542 if (args
[2][0] == '/' && args
[2][1] == 0)
545 g_menu_path_buf
[0] = 0;
546 fs
->fs_dir(dev
, "/", ventoy_browser_iterate_dir
, NULL
);
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
);
553 grub_device_close(dev
);
555 if (g_tree_view_menu_style
== 0)
557 browser_ssprintf(&mbuf
, "menuentry \"%-10s [(%s)%s/..]\" --class=\"vtoyret\" VTOY_RET {\n "
558 " echo 'return ...' \n}\n", "<--", args
[0], g_menu_path_buf
);
562 browser_ssprintf(&mbuf
, "menuentry \"[(%s)%s/..]\" --class=\"vtoyret\" VTOY_RET {\n "
563 " echo 'return ...' \n}\n", args
[0], g_menu_path_buf
);
566 for (i
= 1; i
>= 0; i
--)
570 node
= ventoy_browser_find_top_node(i
);
573 browser_ssprintf(&mbuf
, "%s", node
->menuentry
);
574 ventoy_browser_mbuf_extend(&mbuf
);
578 node
->prev
->next
= node
->next
;
582 node
->next
->prev
= node
->prev
;
585 if (node
== g_browser_list
)
587 g_browser_list
= node
->next
;
597 g_browser_list
= NULL
;
599 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)mbuf
.buf
, mbuf
.pos
);
600 grub_script_execute_sourcecode(cfgfile
);
602 ventoy_browser_mbuf_free(&mbuf
);
603 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
606 grub_err_t
ventoy_cmd_browser_disk(grub_extcmd_context_t ctxt
, int argc
, char **args
)
615 if (!ventoy_browser_mbuf_alloc(&mbuf
))
620 g_vtoy_dev
= grub_env_get("vtoydev");
622 if (g_tree_view_menu_style
== 0)
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"));
630 browser_ssprintf(&mbuf
, "menuentry \"[%s]\" --class=\"vtoyret\" VTOY_RET {\n "
631 " echo 'return ...' \n}\n",
632 ventoy_get_vmenu_title("VTLANG_BROWER_RETURN"));
635 grub_disk_dev_iterate(ventoy_browser_iterate_disk
, &mbuf
);
637 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)mbuf
.buf
, mbuf
.pos
);
638 grub_script_execute_sourcecode(cfgfile
);
640 ventoy_browser_mbuf_free(&mbuf
);
641 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);