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
< 2 && 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 grub_snprintf(title
, sizeof(title
), "%-10s (%s,%s%d) [%s] %s %s",
175 "DISK", disk
->name
, partition
->msdostype
== 0xee ? "gpt" : "msdos",
176 partition
->number
+ 1, (Label
? Label
: ""), fs
->name
,
177 grub_get_human_size(partition
->len
<< disk
->log_sector_size
, GRUB_HUMAN_SIZE_SHORT
));
179 if (ventoy_get_fs_type(fs
->name
) >= ventoy_fs_max
)
181 browser_ssprintf(mbuf
, "menuentry \"%s\" --class=vtoydisk {\n"
182 " echo \"unsupported file system type!\" \n"
189 browser_ssprintf(mbuf
, "menuentry \"%s\" --class=vtoydisk {\n"
190 " vt_browser_dir %s,%d 0x%lx /\n"
192 title
, disk
->name
, partition
->number
+ 1, (ulong
)fs
);
195 ventoy_browser_mbuf_extend(mbuf
);
200 static int ventoy_browser_iterate_disk(const char *name
, void *data
)
209 disk
= grub_disk_open(name
);
212 grub_partition_iterate(disk
, ventoy_browser_iterate_partition
, data
);
213 grub_disk_close(disk
);
219 static int ventoy_browser_valid_dirname(const char *name
, int len
)
221 if ((len
== 1 && name
[0] == '.') ||
222 (len
== 2 && name
[0] == '.' && name
[1] == '.'))
227 if (!ventoy_img_name_valid(name
, len
))
234 if (0 == grub_strncmp(name
, "$RECYCLE.BIN", 12) ||
235 0 == grub_strncasecmp(name
, "$Extend", 7))
241 if (len
== 25 && grub_strncmp(name
, "System Volume Information", 25) == 0)
249 static int ventoy_browser_valid_filename(const char *filename
, int len
, int *type
)
256 if (FILE_FLT(ISO
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".iso"))
258 *type
= img_type_iso
;
260 else if (FILE_FLT(WIM
) && g_wimboot_enable
&& (0 == grub_strcasecmp(filename
+ len
- 4, ".wim")))
262 *type
= img_type_wim
;
264 else if (FILE_FLT(VHD
) && g_vhdboot_enable
&& (0 == grub_strcasecmp(filename
+ len
- 4, ".vhd") ||
265 (len
>= 5 && 0 == grub_strcasecmp(filename
+ len
- 5, ".vhdx"))))
267 *type
= img_type_vhd
;
269 #ifdef GRUB_MACHINE_EFI
270 else if (FILE_FLT(EFI
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".efi"))
272 *type
= img_type_efi
;
275 else if (FILE_FLT(IMG
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".img"))
277 if (len
== 18 && grub_strncmp(filename
, "ventoy_", 7) == 0)
279 if (grub_strncmp(filename
+ 7, "wimboot", 7) == 0 ||
280 grub_strncmp(filename
+ 7, "vhdboot", 7) == 0)
285 *type
= img_type_img
;
287 else if (FILE_FLT(VTOY
) && len
>= 5 && 0 == grub_strcasecmp(filename
+ len
- 5, ".vtoy"))
289 *type
= img_type_vtoy
;
296 if (g_filt_dot_underscore_file
&& filename
[0] == '.' && filename
[1] == '_')
304 static int ventoy_browser_iterate_dir(const char *filename
, const struct grub_dirhook_info
*info
, void *data
)
312 len
= grub_strlen(filename
);
316 if (!ventoy_browser_valid_dirname(filename
, len
))
321 node
= grub_zalloc(sizeof(browser_node
));
328 grub_strncpy(node
->filename
, filename
, sizeof(node
->filename
));
329 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
330 "menuentry \"%-10s [%s]\" --class=vtoydir {\n"
331 " vt_browser_dir %s 0x%lx \"%s/%s\"\n"
333 "DIR", filename
, g_menu_device
, (ulong
)g_menu_fs
, g_menu_path_buf
, filename
);
337 grub_uint64_t fsize
= info
->size
;
339 if (!ventoy_browser_valid_filename(filename
, len
, &type
))
344 node
= grub_zalloc(sizeof(browser_node
));
352 struct grub_file file
;
354 grub_memset(&file
, 0, sizeof(file
));
355 file
.device
= g_menu_dev
;
356 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
), "%s/%s", g_menu_path_buf
, filename
);
357 if (g_menu_fs
->fs_open(&file
, node
->menuentry
) == GRUB_ERR_NONE
)
360 g_menu_fs
->fs_close(&file
);
365 grub_strncpy(node
->filename
, filename
, sizeof(node
->filename
));
366 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
367 "menuentry \"%-10s %s\" --class=%s {\n"
368 " vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
369 " %s_common_menuentry\n"
370 " vt_reset_fake_vlnk\n"
372 grub_get_human_size(fsize
, GRUB_HUMAN_SIZE_SHORT
), filename
, g_menu_class
[type
],
373 g_menu_device
, g_menu_path_buf
, filename
, g_menu_prefix
[type
], (ulonglong
)fsize
,
374 g_menu_prefix
[type
]);
378 node
->next
= g_browser_list
;
381 g_browser_list
->prev
= node
;
383 g_browser_list
= node
;
388 grub_err_t
ventoy_cmd_browser_dir(grub_extcmd_context_t ctxt
, int argc
, char **args
)
400 if (!ventoy_browser_mbuf_alloc(&mbuf
))
405 fs
= (grub_fs_t
)grub_strtoul(args
[1], NULL
, 16);
408 debug("Invalid fs %s\n", args
[1]);
412 dev
= grub_device_open(args
[0]);
415 debug("Failed to open device %s\n", args
[0]);
420 g_menu_device
= args
[0];
422 g_browser_list
= NULL
;
424 if (args
[2][0] == '/' && args
[2][1] == 0)
427 g_menu_path_buf
[0] = 0;
428 fs
->fs_dir(dev
, "/", ventoy_browser_iterate_dir
, NULL
);
432 g_menu_path_len
= grub_snprintf(g_menu_path_buf
, sizeof(g_menu_path_buf
), "%s", args
[2]);
433 fs
->fs_dir(dev
, g_menu_path_buf
, ventoy_browser_iterate_dir
, NULL
);
435 grub_device_close(dev
);
437 browser_ssprintf(&mbuf
, "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n "
438 " echo 'return ...' \n}\n", "<--");
440 for (i
= 1; i
>= 0; i
--)
444 node
= ventoy_browser_find_top_node(i
);
447 browser_ssprintf(&mbuf
, "%s", node
->menuentry
);
448 ventoy_browser_mbuf_extend(&mbuf
);
452 node
->prev
->next
= node
->next
;
456 node
->next
->prev
= node
->prev
;
459 if (node
== g_browser_list
)
461 g_browser_list
= node
->next
;
471 g_browser_list
= NULL
;
473 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)mbuf
.buf
, mbuf
.pos
);
474 grub_script_execute_sourcecode(cfgfile
);
476 ventoy_browser_mbuf_free(&mbuf
);
477 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
480 grub_err_t
ventoy_cmd_browser_disk(grub_extcmd_context_t ctxt
, int argc
, char **args
)
489 if (!ventoy_browser_mbuf_alloc(&mbuf
))
494 g_vtoy_dev
= grub_env_get("vtoydev");
496 browser_ssprintf(&mbuf
, "menuentry \"%-10s [Return]\" --class=\"vtoyret\" VTOY_RET {\n "
497 " echo 'return ...' \n}\n", "<--");
499 grub_disk_dev_iterate(ventoy_browser_iterate_disk
, &mbuf
);
501 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)mbuf
.buf
, mbuf
.pos
);
502 grub_script_execute_sourcecode(cfgfile
);
504 ventoy_browser_mbuf_free(&mbuf
);
505 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);