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_check_filename(const char *filename
, int len
, int *type
)
148 if (FILE_FLT(ISO
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".iso"))
150 *type
= img_type_iso
;
152 else if (FILE_FLT(WIM
) && g_wimboot_enable
&& (0 == grub_strcasecmp(filename
+ len
- 4, ".wim")))
154 *type
= img_type_wim
;
156 else if (FILE_FLT(VHD
) && g_vhdboot_enable
&& (0 == grub_strcasecmp(filename
+ len
- 4, ".vhd") ||
157 (len
>= 5 && 0 == grub_strcasecmp(filename
+ len
- 5, ".vhdx"))))
159 *type
= img_type_vhd
;
161 #ifdef GRUB_MACHINE_EFI
162 else if (FILE_FLT(EFI
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".efi"))
164 *type
= img_type_efi
;
167 else if (FILE_FLT(IMG
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".img"))
169 if (len
== 18 && grub_strncmp(filename
, "ventoy_", 7) == 0)
171 if (grub_strncmp(filename
+ 7, "wimboot", 7) == 0 ||
172 grub_strncmp(filename
+ 7, "vhdboot", 7) == 0)
177 *type
= img_type_img
;
179 else if (FILE_FLT(VTOY
) && len
>= 5 && 0 == grub_strcasecmp(filename
+ len
- 5, ".vtoy"))
181 *type
= img_type_vtoy
;
188 if (g_filt_dot_underscore_file
&& filename
[0] == '.' && filename
[1] == '_')
197 static int ventoy_browser_iterate_partition(struct grub_disk
*disk
, const grub_partition_t partition
, void *data
)
204 browser_mbuf
*mbuf
= (browser_mbuf
*)data
;
208 if (partition
->number
< 2 && g_vtoy_dev
&& grub_strcmp(disk
->name
, g_vtoy_dev
) == 0)
213 grub_snprintf(partname
, sizeof(partname
) - 1, "%s,%d", disk
->name
, partition
->number
+ 1);
215 dev
= grub_device_open(partname
);
221 fs
= grub_fs_probe(dev
);
224 grub_device_close(dev
);
228 fs
->fs_label(dev
, &Label
);
230 grub_snprintf(title
, sizeof(title
), "%-10s (%s,%s%d) [%s] %s %s",
231 "DISK", disk
->name
, partition
->msdostype
== 0xee ? "gpt" : "msdos",
232 partition
->number
+ 1, (Label
? Label
: ""), fs
->name
,
233 grub_get_human_size(partition
->len
<< disk
->log_sector_size
, GRUB_HUMAN_SIZE_SHORT
));
235 if (ventoy_get_fs_type(fs
->name
) >= ventoy_fs_max
)
237 browser_ssprintf(mbuf
, "menuentry \"%s\" --class=vtoydisk {\n"
238 " echo \"unsupported file system type!\" \n"
245 browser_ssprintf(mbuf
, "menuentry \"%s\" --class=vtoydisk {\n"
246 " vt_browser_dir %s,%d 0x%lx /\n"
248 title
, disk
->name
, partition
->number
+ 1, (ulong
)fs
);
251 ventoy_browser_mbuf_extend(mbuf
);
256 static int ventoy_browser_iterate_disk(const char *name
, void *data
)
265 disk
= grub_disk_open(name
);
268 grub_partition_iterate(disk
, ventoy_browser_iterate_partition
, data
);
269 grub_disk_close(disk
);
275 static int ventoy_browser_iterate_dir(const char *filename
, const struct grub_dirhook_info
*info
, void *data
)
283 len
= grub_strlen(filename
);
287 if ((len
== 1 && filename
[0] == '.') ||
288 (len
== 2 && filename
[0] == '.' && filename
[1] == '.'))
293 if (!ventoy_img_name_valid(filename
, len
))
298 if (filename
[0] == '$')
300 if (0 == grub_strncmp(filename
, "$RECYCLE.BIN", 12) ||
301 0 == grub_strncasecmp(filename
, "$Extend", 7))
307 node
= grub_zalloc(sizeof(browser_node
));
314 grub_strncpy(node
->filename
, filename
, sizeof(node
->filename
));
315 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
316 "menuentry \"%-10s [%s]\" --class=vtoydir {\n"
317 " vt_browser_dir %s 0x%lx \"%s/%s\"\n"
319 "DIR", filename
, g_menu_device
, (ulong
)g_menu_fs
, g_menu_path_buf
, filename
);
323 grub_uint64_t fsize
= info
->size
;
325 if (ventoy_browser_check_filename(filename
, len
, &type
) == 0)
330 node
= grub_zalloc(sizeof(browser_node
));
338 struct grub_file file
;
340 grub_memset(&file
, 0, sizeof(file
));
341 file
.device
= g_menu_dev
;
342 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
), "%s/%s", g_menu_path_buf
, filename
);
343 if (g_menu_fs
->fs_open(&file
, node
->menuentry
) == GRUB_ERR_NONE
)
346 g_menu_fs
->fs_close(&file
);
351 grub_strncpy(node
->filename
, filename
, sizeof(node
->filename
));
352 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
353 "menuentry \"%-10s %s\" --class=%s {\n"
354 " vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
355 " %s_common_menuentry\n"
356 " vt_reset_fake_vlnk\n"
358 grub_get_human_size(fsize
, GRUB_HUMAN_SIZE_SHORT
), filename
, g_menu_class
[type
],
359 g_menu_device
, g_menu_path_buf
, filename
, g_menu_prefix
[type
], (ulonglong
)fsize
,
360 g_menu_prefix
[type
]);
364 node
->next
= g_browser_list
;
367 g_browser_list
->prev
= node
;
369 g_browser_list
= node
;
374 grub_err_t
ventoy_cmd_browser_dir(grub_extcmd_context_t ctxt
, int argc
, char **args
)
386 if (!ventoy_browser_mbuf_alloc(&mbuf
))
391 fs
= (grub_fs_t
)grub_strtoul(args
[1], NULL
, 16);
394 debug("Invalid fs %s\n", args
[1]);
398 dev
= grub_device_open(args
[0]);
401 debug("Failed to open device %s\n", args
[0]);
406 g_menu_device
= args
[0];
408 g_browser_list
= NULL
;
410 if (args
[2][0] == '/' && args
[2][1] == 0)
413 g_menu_path_buf
[0] = 0;
414 fs
->fs_dir(dev
, "/", ventoy_browser_iterate_dir
, NULL
);
418 g_menu_path_len
= grub_snprintf(g_menu_path_buf
, sizeof(g_menu_path_buf
), "%s", args
[2]);
419 fs
->fs_dir(dev
, g_menu_path_buf
, ventoy_browser_iterate_dir
, NULL
);
421 grub_device_close(dev
);
423 browser_ssprintf(&mbuf
, "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n "
424 " echo 'return ...' \n}\n", "<--");
426 for (i
= 1; i
>= 0; i
--)
430 node
= ventoy_browser_find_top_node(i
);
433 browser_ssprintf(&mbuf
, "%s", node
->menuentry
);
434 ventoy_browser_mbuf_extend(&mbuf
);
438 node
->prev
->next
= node
->next
;
442 node
->next
->prev
= node
->prev
;
445 if (node
== g_browser_list
)
447 g_browser_list
= node
->next
;
457 g_browser_list
= NULL
;
459 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)mbuf
.buf
, mbuf
.pos
);
460 grub_script_execute_sourcecode(cfgfile
);
462 ventoy_browser_mbuf_free(&mbuf
);
463 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
466 grub_err_t
ventoy_cmd_browser_disk(grub_extcmd_context_t ctxt
, int argc
, char **args
)
475 if (!ventoy_browser_mbuf_alloc(&mbuf
))
480 g_vtoy_dev
= grub_env_get("vtoydev");
482 browser_ssprintf(&mbuf
, "menuentry \"%-10s [Return]\" --class=\"vtoyret\" VTOY_RET {\n "
483 " echo 'return ...' \n}\n", "<--");
485 grub_disk_dev_iterate(ventoy_browser_iterate_disk
, &mbuf
);
487 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)mbuf
.buf
, mbuf
.pos
);
488 grub_script_execute_sourcecode(cfgfile
);
490 ventoy_browser_mbuf_free(&mbuf
);
491 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);