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 grub_fs_t g_menu_fs
= NULL
;
44 static char *g_menu_device
= NULL
;
45 static char g_menu_path_buf
[1024];
46 static int g_menu_path_len
= 0;
47 static browser_node
*g_browser_list
= NULL
;
49 static int ventoy_browser_strcmp(char *str1
, char *str2
)
55 for (s1
= str1
, s2
= str2
; *s1
&& *s2
; s1
++, s2
++)
60 if (0 == g_sort_case_sensitive
)
82 static int ventoy_browser_mbuf_alloc(browser_mbuf
*mbuf
)
84 grub_memset(mbuf
, 0, sizeof(browser_mbuf
));
85 mbuf
->buf
= grub_malloc(BROWSER_MENU_BUF
);
92 mbuf
->max
= BROWSER_MENU_BUF
;
96 static void ventoy_browser_mbuf_free(browser_mbuf
*mbuf
)
99 grub_check_free(mbuf
->buf
)
102 static int ventoy_browser_mbuf_extend(browser_mbuf
*mbuf
)
104 if (mbuf
->max
- mbuf
->pos
<= VTOY_SIZE_1KB
)
106 mbuf
->max
+= BROWSER_MENU_BUF
;
107 mbuf
->buf
= grub_realloc(mbuf
->buf
, mbuf
->max
);
113 static browser_node
* ventoy_browser_find_top_node(int dir
)
115 browser_node
*node
= NULL
;
116 browser_node
*sel
= NULL
;
118 for (node
= g_browser_list
; node
; node
= node
->next
)
120 if (node
->dir
== dir
)
124 if (ventoy_browser_strcmp(sel
->filename
, node
->filename
) > 0)
139 static int ventoy_browser_check_filename(const char *filename
, int len
, int *type
)
146 if (FILE_FLT(ISO
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".iso"))
148 *type
= img_type_iso
;
150 else if (FILE_FLT(WIM
) && g_wimboot_enable
&& (0 == grub_strcasecmp(filename
+ len
- 4, ".wim")))
152 *type
= img_type_wim
;
154 else if (FILE_FLT(VHD
) && g_vhdboot_enable
&& (0 == grub_strcasecmp(filename
+ len
- 4, ".vhd") ||
155 (len
>= 5 && 0 == grub_strcasecmp(filename
+ len
- 5, ".vhdx"))))
157 *type
= img_type_vhd
;
159 #ifdef GRUB_MACHINE_EFI
160 else if (FILE_FLT(EFI
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".efi"))
162 *type
= img_type_efi
;
165 else if (FILE_FLT(IMG
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".img"))
167 if (len
== 18 && grub_strncmp(filename
, "ventoy_", 7) == 0)
169 if (grub_strncmp(filename
+ 7, "wimboot", 7) == 0 ||
170 grub_strncmp(filename
+ 7, "vhdboot", 7) == 0)
175 *type
= img_type_img
;
177 else if (FILE_FLT(VTOY
) && len
>= 5 && 0 == grub_strcasecmp(filename
+ len
- 5, ".vtoy"))
179 *type
= img_type_vtoy
;
186 if (g_filt_dot_underscore_file
&& filename
[0] == '.' && filename
[1] == '_')
195 static int ventoy_browser_iterate_partition(struct grub_disk
*disk
, const grub_partition_t partition
, void *data
)
202 browser_mbuf
*mbuf
= (browser_mbuf
*)data
;
206 grub_snprintf(partname
, sizeof(partname
) - 1, "%s,%d", disk
->name
, partition
->number
+ 1);
208 dev
= grub_device_open(partname
);
214 fs
= grub_fs_probe(dev
);
217 grub_device_close(dev
);
221 fs
->fs_label(dev
, &Label
);
223 grub_snprintf(title
, sizeof(title
), "%-10s (%s,%s%d) [%s] %s %s",
224 "DISK", disk
->name
, partition
->msdostype
== 0xee ? "gpt" : "msdos",
225 partition
->number
+ 1, (Label
? Label
: ""), fs
->name
,
226 grub_get_human_size(partition
->len
<< disk
->log_sector_size
, GRUB_HUMAN_SIZE_SHORT
));
228 if (ventoy_get_fs_type(fs
->name
) >= ventoy_fs_max
)
230 browser_ssprintf(mbuf
, "menuentry \"%s\" --class=vtoydisk {\n"
231 " echo \"unsupported file system type!\" \n"
238 browser_ssprintf(mbuf
, "menuentry \"%s\" --class=vtoydisk {\n"
239 " vt_browser_dir %s,%d 0x%lx /\n"
241 title
, disk
->name
, partition
->number
+ 1, (ulong
)fs
);
244 ventoy_browser_mbuf_extend(mbuf
);
250 static int ventoy_browser_iterate_disk(const char *name
, void *data
)
254 grub_uint32_t selfsig
;
256 grub_memcpy(&selfsig
, g_ventoy_part_info
->MBR
.BootCode
+ 0x1b8, 4);
263 disk
= grub_disk_open(name
);
266 grub_disk_read(disk
, 0, 0x1b8, 4, &sig
);
268 /* skip ventoy device self */
271 grub_partition_iterate(disk
, ventoy_browser_iterate_partition
, data
);
274 grub_disk_close(disk
);
280 static int ventoy_browser_iterate_dir(const char *filename
, const struct grub_dirhook_info
*info
, void *data
)
288 len
= grub_strlen(filename
);
292 if ((len
== 1 && filename
[0] == '.') ||
293 (len
== 2 && filename
[0] == '.' && filename
[1] == '.'))
298 if (!ventoy_img_name_valid(filename
, len
))
303 if (filename
[0] == '$')
305 if (0 == grub_strncmp(filename
, "$RECYCLE.BIN", 12) ||
306 0 == grub_strncasecmp(filename
, "$Extend", 7))
312 node
= grub_zalloc(sizeof(browser_node
));
319 grub_strncpy(node
->filename
, filename
, sizeof(node
->filename
));
320 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
321 "menuentry \"%-10s [%s]\" --class=vtoydir {\n"
322 " vt_browser_dir %s 0x%lx \"%s/%s\"\n"
324 "DIR", filename
, g_menu_device
, (ulong
)g_menu_fs
, g_menu_path_buf
, filename
);
329 grub_file_t file
= NULL
;
331 if (ventoy_browser_check_filename(filename
, len
, &type
) == 0)
339 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "(%s)%s/%s", g_menu_device
, g_menu_path_buf
, filename
);
346 grub_file_close(file
);
349 if (fsize
< VTOY_FILT_MIN_FILE_SIZE
)
354 node
= grub_zalloc(sizeof(browser_node
));
361 grub_strncpy(node
->filename
, filename
, sizeof(node
->filename
));
362 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
363 "menuentry \"%-10s %s\" --class=%s {\n"
364 " vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
365 " %s_common_menuentry\n"
366 " vt_reset_fake_vlnk\n"
368 grub_get_human_size(fsize
, GRUB_HUMAN_SIZE_SHORT
), filename
, g_menu_class
[type
],
369 g_menu_device
, g_menu_path_buf
, filename
, g_menu_prefix
[type
], (ulonglong
)fsize
,
370 g_menu_prefix
[type
]);
375 node
->next
= g_browser_list
;
378 g_browser_list
->prev
= node
;
380 g_browser_list
= node
;
385 grub_err_t
ventoy_cmd_browser_dir(grub_extcmd_context_t ctxt
, int argc
, char **args
)
397 if (!ventoy_browser_mbuf_alloc(&mbuf
))
402 fs
= (grub_fs_t
)grub_strtoul(args
[1], NULL
, 16);
405 debug("Invalid fs %s\n", args
[1]);
409 dev
= grub_device_open(args
[0]);
412 debug("Failed to open device %s\n", args
[0]);
417 g_menu_device
= args
[0];
418 g_browser_list
= NULL
;
420 if (args
[2][0] == '/' && args
[2][1] == 0)
423 g_menu_path_buf
[0] = 0;
424 fs
->fs_dir(dev
, "/", ventoy_browser_iterate_dir
, NULL
);
428 g_menu_path_len
= grub_snprintf(g_menu_path_buf
, sizeof(g_menu_path_buf
), "%s", args
[2]);
429 fs
->fs_dir(dev
, g_menu_path_buf
, ventoy_browser_iterate_dir
, NULL
);
431 grub_device_close(dev
);
433 browser_ssprintf(&mbuf
, "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n "
434 " echo 'return ...' \n}\n", "<--");
436 for (i
= 1; i
>= 0; i
--)
440 node
= ventoy_browser_find_top_node(i
);
443 grub_printf("Find Node <%s>\n", node
->filename
);
444 browser_ssprintf(&mbuf
, "%s", node
->menuentry
);
447 node
->prev
->next
= node
->next
;
451 node
->next
->prev
= node
->prev
;
454 if (node
== g_browser_list
)
456 g_browser_list
= node
->next
;
466 g_browser_list
= NULL
;
468 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)mbuf
.buf
, mbuf
.pos
);
469 grub_script_execute_sourcecode(cfgfile
);
471 ventoy_browser_mbuf_free(&mbuf
);
472 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
475 grub_err_t
ventoy_cmd_browser_disk(grub_extcmd_context_t ctxt
, int argc
, char **args
)
484 if (!ventoy_browser_mbuf_alloc(&mbuf
))
489 browser_ssprintf(&mbuf
, "menuentry \"%-10s [Return]\" --class=\"vtoyret\" VTOY_RET {\n "
490 " echo 'return ...' \n}\n", "<--");
492 grub_disk_dev_iterate(ventoy_browser_iterate_disk
, &mbuf
);
494 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)mbuf
.buf
, mbuf
.pos
);
495 grub_script_execute_sourcecode(cfgfile
);
497 ventoy_browser_mbuf_free(&mbuf
);
498 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);