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 (g_tree_view_menu_style
== 0)
176 grub_snprintf(title
, sizeof(title
), "%-10s (%s,%s%d) [%s] %s %s",
177 "DISK", disk
->name
, partition
->msdostype
== 0xee ? "gpt" : "msdos",
178 partition
->number
+ 1, (Label
? Label
: ""), fs
->name
,
179 grub_get_human_size(partition
->len
<< disk
->log_sector_size
, GRUB_HUMAN_SIZE_SHORT
));
183 grub_snprintf(title
, sizeof(title
), "(%s,%s%d) [%s] %s %s",
184 disk
->name
, partition
->msdostype
== 0xee ? "gpt" : "msdos",
185 partition
->number
+ 1, (Label
? Label
: ""), fs
->name
,
186 grub_get_human_size(partition
->len
<< disk
->log_sector_size
, GRUB_HUMAN_SIZE_SHORT
));
189 if (ventoy_get_fs_type(fs
->name
) >= ventoy_fs_max
)
191 browser_ssprintf(mbuf
, "menuentry \"%s\" --class=vtoydisk {\n"
192 " echo \"unsupported file system type!\" \n"
199 browser_ssprintf(mbuf
, "menuentry \"%s\" --class=vtoydisk {\n"
200 " vt_browser_dir %s,%d 0x%lx /\n"
202 title
, disk
->name
, partition
->number
+ 1, (ulong
)fs
);
205 ventoy_browser_mbuf_extend(mbuf
);
210 static int ventoy_browser_iterate_disk(const char *name
, void *data
)
219 disk
= grub_disk_open(name
);
222 grub_partition_iterate(disk
, ventoy_browser_iterate_partition
, data
);
223 grub_disk_close(disk
);
229 static int ventoy_browser_valid_dirname(const char *name
, int len
)
231 if ((len
== 1 && name
[0] == '.') ||
232 (len
== 2 && name
[0] == '.' && name
[1] == '.'))
237 if (!ventoy_img_name_valid(name
, len
))
244 if (0 == grub_strncmp(name
, "$RECYCLE.BIN", 12) ||
245 0 == grub_strncasecmp(name
, "$Extend", 7))
251 if (len
== 25 && grub_strncmp(name
, "System Volume Information", 25) == 0)
259 static int ventoy_browser_valid_filename(const char *filename
, int len
, int *type
)
266 if (FILE_FLT(ISO
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".iso"))
268 *type
= img_type_iso
;
270 else if (FILE_FLT(WIM
) && g_wimboot_enable
&& (0 == grub_strcasecmp(filename
+ len
- 4, ".wim")))
272 *type
= img_type_wim
;
274 else if (FILE_FLT(VHD
) && g_vhdboot_enable
&& (0 == grub_strcasecmp(filename
+ len
- 4, ".vhd") ||
275 (len
>= 5 && 0 == grub_strcasecmp(filename
+ len
- 5, ".vhdx"))))
277 *type
= img_type_vhd
;
279 #ifdef GRUB_MACHINE_EFI
280 else if (FILE_FLT(EFI
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".efi"))
282 *type
= img_type_efi
;
285 else if (FILE_FLT(IMG
) && 0 == grub_strcasecmp(filename
+ len
- 4, ".img"))
287 if (len
== 18 && grub_strncmp(filename
, "ventoy_", 7) == 0)
289 if (grub_strncmp(filename
+ 7, "wimboot", 7) == 0 ||
290 grub_strncmp(filename
+ 7, "vhdboot", 7) == 0)
295 *type
= img_type_img
;
297 else if (FILE_FLT(VTOY
) && len
>= 5 && 0 == grub_strcasecmp(filename
+ len
- 5, ".vtoy"))
299 *type
= img_type_vtoy
;
306 if (g_filt_dot_underscore_file
&& filename
[0] == '.' && filename
[1] == '_')
314 static int ventoy_browser_iterate_dir(const char *filename
, const struct grub_dirhook_info
*info
, void *data
)
322 len
= grub_strlen(filename
);
326 if (!ventoy_browser_valid_dirname(filename
, len
))
331 node
= grub_zalloc(sizeof(browser_node
));
338 grub_strncpy(node
->filename
, filename
, sizeof(node
->filename
));
340 if (g_tree_view_menu_style
== 0)
342 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
343 "menuentry \"%-10s [%s]\" --class=vtoydir {\n"
344 " vt_browser_dir %s 0x%lx \"%s/%s\"\n"
346 "DIR", filename
, g_menu_device
, (ulong
)g_menu_fs
, g_menu_path_buf
, filename
);
350 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
351 "menuentry \"[%s]\" --class=vtoydir {\n"
352 " vt_browser_dir %s 0x%lx \"%s/%s\"\n"
354 filename
, g_menu_device
, (ulong
)g_menu_fs
, g_menu_path_buf
, filename
);
359 grub_uint64_t fsize
= info
->size
;
361 if (!ventoy_browser_valid_filename(filename
, len
, &type
))
366 node
= grub_zalloc(sizeof(browser_node
));
374 struct grub_file file
;
376 grub_memset(&file
, 0, sizeof(file
));
377 file
.device
= g_menu_dev
;
378 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
), "%s/%s", g_menu_path_buf
, filename
);
379 if (g_menu_fs
->fs_open(&file
, node
->menuentry
) == GRUB_ERR_NONE
)
382 g_menu_fs
->fs_close(&file
);
387 grub_strncpy(node
->filename
, filename
, sizeof(node
->filename
));
389 if (g_tree_view_menu_style
== 0)
391 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
392 "menuentry \"%-10s %s\" --class=%s {\n"
393 " vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
394 " %s_common_menuentry\n"
395 " vt_reset_fake_vlnk\n"
397 grub_get_human_size(fsize
, GRUB_HUMAN_SIZE_SHORT
), filename
, g_menu_class
[type
],
398 g_menu_device
, g_menu_path_buf
, filename
, g_menu_prefix
[type
], (ulonglong
)fsize
,
399 g_menu_prefix
[type
]);
403 grub_snprintf(node
->menuentry
, sizeof(node
->menuentry
),
404 "menuentry \"%s\" --class=%s {\n"
405 " vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
406 " %s_common_menuentry\n"
407 " vt_reset_fake_vlnk\n"
409 filename
, g_menu_class
[type
],
410 g_menu_device
, g_menu_path_buf
, filename
, g_menu_prefix
[type
], (ulonglong
)fsize
,
411 g_menu_prefix
[type
]);
416 node
->next
= g_browser_list
;
419 g_browser_list
->prev
= node
;
421 g_browser_list
= node
;
426 static grub_err_t
ventoy_browser_iso_part(void)
434 cfglen
= g_tree_script_pos
- g_tree_script_pre
;
435 buflen
= cfglen
+ 512;
436 buffer
= grub_malloc(buflen
);
442 if (g_tree_view_menu_style
== 0)
444 pos
= grub_snprintf(buffer
, buflen
, "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n "
445 " echo 'return ...' \n}\n", "<--");
449 pos
= grub_snprintf(buffer
, buflen
, "menuentry \"[../]\" --class=\"vtoyret\" VTOY_RET {\n "
450 " echo 'return ...' \n}\n");
453 grub_memcpy(buffer
+ pos
, g_tree_script_buf
+ g_tree_script_pre
, cfglen
);
456 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)buffer
, pos
);
457 grub_script_execute_sourcecode(cfgfile
);
460 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
463 grub_err_t
ventoy_cmd_browser_dir(grub_extcmd_context_t ctxt
, int argc
, char **args
)
475 if (args
[2][0] == '/' && args
[2][1] == 0)
477 grub_snprintf(cfgfile
, sizeof(cfgfile
), "(%s)", args
[0]);
478 if (grub_strcmp(cfgfile
, g_iso_path
) == 0)
480 return ventoy_browser_iso_part();
484 if (!ventoy_browser_mbuf_alloc(&mbuf
))
489 fs
= (grub_fs_t
)grub_strtoul(args
[1], NULL
, 16);
492 debug("Invalid fs %s\n", args
[1]);
496 dev
= grub_device_open(args
[0]);
499 debug("Failed to open device %s\n", args
[0]);
504 g_menu_device
= args
[0];
506 g_browser_list
= NULL
;
508 if (args
[2][0] == '/' && args
[2][1] == 0)
511 g_menu_path_buf
[0] = 0;
512 fs
->fs_dir(dev
, "/", ventoy_browser_iterate_dir
, NULL
);
516 g_menu_path_len
= grub_snprintf(g_menu_path_buf
, sizeof(g_menu_path_buf
), "%s", args
[2]);
517 fs
->fs_dir(dev
, g_menu_path_buf
, ventoy_browser_iterate_dir
, NULL
);
519 grub_device_close(dev
);
521 if (g_tree_view_menu_style
== 0)
523 browser_ssprintf(&mbuf
, "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n "
524 " echo 'return ...' \n}\n", "<--");
528 browser_ssprintf(&mbuf
, "menuentry \"[../]\" --class=\"vtoyret\" VTOY_RET {\n "
529 " echo 'return ...' \n}\n");
532 for (i
= 1; i
>= 0; i
--)
536 node
= ventoy_browser_find_top_node(i
);
539 browser_ssprintf(&mbuf
, "%s", node
->menuentry
);
540 ventoy_browser_mbuf_extend(&mbuf
);
544 node
->prev
->next
= node
->next
;
548 node
->next
->prev
= node
->prev
;
551 if (node
== g_browser_list
)
553 g_browser_list
= node
->next
;
563 g_browser_list
= NULL
;
565 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)mbuf
.buf
, mbuf
.pos
);
566 grub_script_execute_sourcecode(cfgfile
);
568 ventoy_browser_mbuf_free(&mbuf
);
569 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
572 grub_err_t
ventoy_cmd_browser_disk(grub_extcmd_context_t ctxt
, int argc
, char **args
)
581 if (!ventoy_browser_mbuf_alloc(&mbuf
))
586 g_vtoy_dev
= grub_env_get("vtoydev");
588 if (g_tree_view_menu_style
== 0)
590 browser_ssprintf(&mbuf
, "menuentry \"%-10s [Return]\" --class=\"vtoyret\" VTOY_RET {\n "
591 " echo 'return ...' \n}\n", "<--");
595 browser_ssprintf(&mbuf
, "menuentry \"[Return]\" --class=\"vtoyret\" VTOY_RET {\n "
596 " echo 'return ...' \n}\n");
599 grub_disk_dev_iterate(ventoy_browser_iterate_disk
, &mbuf
);
601 grub_snprintf(cfgfile
, sizeof(cfgfile
), "configfile mem:0x%lx:size:%d", (ulong
)mbuf
.buf
, mbuf
.pos
);
602 grub_script_execute_sourcecode(cfgfile
);
604 ventoy_browser_mbuf_free(&mbuf
);
605 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);