]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_browser.c
fad90014520953e17052b79896c7f7b94a443f68
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / ventoy / ventoy_browser.c
1 /******************************************************************************
2 * ventoy_browser.c
3 *
4 * Copyright (c) 2022, longpanda <admin@ventoy.net>
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 */
20 #include <grub/types.h>
21 #include <grub/misc.h>
22 #include <grub/mm.h>
23 #include <grub/err.h>
24 #include <grub/dl.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>
34 #include <grub/net.h>
35 #include <grub/time.h>
36 #include <grub/ventoy.h>
37 #include "ventoy_def.h"
38
39 GRUB_MOD_LICENSE ("GPLv3+");
40
41 #define BROWSER_MENU_BUF 65536
42
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;
50
51 static int ventoy_browser_strcmp(char *str1, char *str2)
52 {
53 char *s1, *s2;
54 int c1 = 0;
55 int c2 = 0;
56
57 for (s1 = str1, s2 = str2; *s1 && *s2; s1++, s2++)
58 {
59 c1 = *s1;
60 c2 = *s2;
61
62 if (0 == g_sort_case_sensitive)
63 {
64 if (grub_islower(c1))
65 {
66 c1 = c1 - 'a' + 'A';
67 }
68
69 if (grub_islower(c2))
70 {
71 c2 = c2 - 'a' + 'A';
72 }
73 }
74
75 if (c1 != c2)
76 {
77 break;
78 }
79 }
80
81 return (c1 - c2);
82 }
83
84 static int ventoy_browser_mbuf_alloc(browser_mbuf *mbuf)
85 {
86 grub_memset(mbuf, 0, sizeof(browser_mbuf));
87 mbuf->buf = grub_malloc(BROWSER_MENU_BUF);
88 if (!mbuf->buf)
89 {
90 return 0;
91 }
92
93 mbuf->pos = 0;
94 mbuf->max = BROWSER_MENU_BUF;
95 return 1;
96 }
97
98 static inline void ventoy_browser_mbuf_free(browser_mbuf *mbuf)
99 {
100 if (mbuf)
101 grub_check_free(mbuf->buf)
102 }
103
104 static inline int ventoy_browser_mbuf_extend(browser_mbuf *mbuf)
105 {
106 if (mbuf->max - mbuf->pos <= VTOY_SIZE_1KB)
107 {
108 mbuf->max += BROWSER_MENU_BUF;
109 mbuf->buf = grub_realloc(mbuf->buf, mbuf->max);
110 }
111
112 return 0;
113 }
114
115 static browser_node * ventoy_browser_find_top_node(int dir)
116 {
117 browser_node *node = NULL;
118 browser_node *sel = NULL;
119
120 for (node = g_browser_list; node; node = node->next)
121 {
122 if (node->dir == dir)
123 {
124 if (sel)
125 {
126 if (ventoy_browser_strcmp(sel->filename, node->filename) > 0)
127 {
128 sel = node;
129 }
130 }
131 else
132 {
133 sel = node;
134 }
135 }
136 }
137
138 return sel;
139 }
140
141 static int ventoy_browser_check_filename(const char *filename, int len, int *type)
142 {
143 if (len < 4)
144 {
145 return 0;
146 }
147
148 if (FILE_FLT(ISO) && 0 == grub_strcasecmp(filename + len - 4, ".iso"))
149 {
150 *type = img_type_iso;
151 }
152 else if (FILE_FLT(WIM) && g_wimboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".wim")))
153 {
154 *type = img_type_wim;
155 }
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"))))
158 {
159 *type = img_type_vhd;
160 }
161 #ifdef GRUB_MACHINE_EFI
162 else if (FILE_FLT(EFI) && 0 == grub_strcasecmp(filename + len - 4, ".efi"))
163 {
164 *type = img_type_efi;
165 }
166 #endif
167 else if (FILE_FLT(IMG) && 0 == grub_strcasecmp(filename + len - 4, ".img"))
168 {
169 if (len == 18 && grub_strncmp(filename, "ventoy_", 7) == 0)
170 {
171 if (grub_strncmp(filename + 7, "wimboot", 7) == 0 ||
172 grub_strncmp(filename + 7, "vhdboot", 7) == 0)
173 {
174 return 0;
175 }
176 }
177 *type = img_type_img;
178 }
179 else if (FILE_FLT(VTOY) && len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vtoy"))
180 {
181 *type = img_type_vtoy;
182 }
183 else
184 {
185 return 0;
186 }
187
188 if (g_filt_dot_underscore_file && filename[0] == '.' && filename[1] == '_')
189 {
190 return 0;
191 }
192
193 return 1;
194 }
195
196
197 static int ventoy_browser_iterate_partition(struct grub_disk *disk, const grub_partition_t partition, void *data)
198 {
199 char partname[64];
200 char title[256];
201 grub_device_t dev;
202 grub_fs_t fs;
203 char *Label = NULL;
204 browser_mbuf *mbuf = (browser_mbuf *)data;
205
206 (void)data;
207
208 if (partition->number < 2 && g_vtoy_dev && grub_strcmp(disk->name, g_vtoy_dev) == 0)
209 {
210 return 0;
211 }
212
213 grub_snprintf(partname, sizeof(partname) - 1, "%s,%d", disk->name, partition->number + 1);
214
215 dev = grub_device_open(partname);
216 if (!dev)
217 {
218 return 0;
219 }
220
221 fs = grub_fs_probe(dev);
222 if (!fs)
223 {
224 grub_device_close(dev);
225 return 0;
226 }
227
228 fs->fs_label(dev, &Label);
229
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));
234
235 if (ventoy_get_fs_type(fs->name) >= ventoy_fs_max)
236 {
237 browser_ssprintf(mbuf, "menuentry \"%s\" --class=vtoydisk {\n"
238 " echo \"unsupported file system type!\" \n"
239 " ventoy_pause\n"
240 "}\n",
241 title);
242 }
243 else
244 {
245 browser_ssprintf(mbuf, "menuentry \"%s\" --class=vtoydisk {\n"
246 " vt_browser_dir %s,%d 0x%lx /\n"
247 "}\n",
248 title, disk->name, partition->number + 1, (ulong)fs);
249 }
250
251 ventoy_browser_mbuf_extend(mbuf);
252
253 return 0;
254 }
255
256 static int ventoy_browser_iterate_disk(const char *name, void *data)
257 {
258 grub_disk_t disk;
259
260 if (name[0] != 'h')
261 {
262 return 0;
263 }
264
265 disk = grub_disk_open(name);
266 if (disk)
267 {
268 grub_partition_iterate(disk, ventoy_browser_iterate_partition, data);
269 grub_disk_close(disk);
270 }
271
272 return 0;
273 }
274
275 static int ventoy_browser_iterate_dir(const char *filename, const struct grub_dirhook_info *info, void *data)
276 {
277 int type;
278 int len;
279 browser_node *node;
280
281 (void)data;
282
283 len = grub_strlen(filename);
284
285 if (info->dir)
286 {
287 if ((len == 1 && filename[0] == '.') ||
288 (len == 2 && filename[0] == '.' && filename[1] == '.'))
289 {
290 return 0;
291 }
292
293 if (!ventoy_img_name_valid(filename, len))
294 {
295 return 0;
296 }
297
298 if (filename[0] == '$')
299 {
300 if (0 == grub_strncmp(filename, "$RECYCLE.BIN", 12) ||
301 0 == grub_strncasecmp(filename, "$Extend", 7))
302 {
303 return 0;
304 }
305 }
306
307 node = grub_zalloc(sizeof(browser_node));
308 if (!node)
309 {
310 return 0;
311 }
312
313 node->dir = 1;
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"
318 "}\n",
319 "DIR", filename, g_menu_device, (ulong)g_menu_fs, g_menu_path_buf, filename);
320 }
321 else
322 {
323 grub_uint64_t fsize = info->size;
324
325 if (ventoy_browser_check_filename(filename, len, &type) == 0)
326 {
327 return 0;
328 }
329
330 node = grub_zalloc(sizeof(browser_node));
331 if (!node)
332 {
333 return 0;
334 }
335
336 if (fsize == 0)
337 {
338 struct grub_file file;
339
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)
344 {
345 fsize = file.size;
346 g_menu_fs->fs_close(&file);
347 }
348 }
349
350 node->dir = 0;
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"
357 "}\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]);
361 }
362
363 node->prev = NULL;
364 node->next = g_browser_list;
365 if (g_browser_list)
366 {
367 g_browser_list->prev = node;
368 }
369 g_browser_list = node;
370
371 return 0;
372 }
373
374 grub_err_t ventoy_cmd_browser_dir(grub_extcmd_context_t ctxt, int argc, char **args)
375 {
376 int i;
377 grub_fs_t fs;
378 grub_device_t dev;
379 char cfgfile[64];
380 browser_node *node;
381 browser_mbuf mbuf;
382
383 (void)ctxt;
384 (void)argc;
385
386 if (!ventoy_browser_mbuf_alloc(&mbuf))
387 {
388 return 1;
389 }
390
391 fs = (grub_fs_t)grub_strtoul(args[1], NULL, 16);
392 if (!fs)
393 {
394 debug("Invalid fs %s\n", args[1]);
395 return 1;
396 }
397
398 dev = grub_device_open(args[0]);
399 if (!dev)
400 {
401 debug("Failed to open device %s\n", args[0]);
402 return 1;
403 }
404
405 g_menu_fs = fs;
406 g_menu_device = args[0];
407 g_menu_dev = dev;
408 g_browser_list = NULL;
409
410 if (args[2][0] == '/' && args[2][1] == 0)
411 {
412 g_menu_path_len = 0;
413 g_menu_path_buf[0] = 0;
414 fs->fs_dir(dev, "/", ventoy_browser_iterate_dir, NULL);
415 }
416 else
417 {
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);
420 }
421 grub_device_close(dev);
422
423 browser_ssprintf(&mbuf, "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n "
424 " echo 'return ...' \n}\n", "<--");
425
426 for (i = 1; i >= 0; i--)
427 {
428 while (1)
429 {
430 node = ventoy_browser_find_top_node(i);
431 if (node)
432 {
433 browser_ssprintf(&mbuf, "%s", node->menuentry);
434 ventoy_browser_mbuf_extend(&mbuf);
435
436 if (node->prev)
437 {
438 node->prev->next = node->next;
439 }
440 if (node->next)
441 {
442 node->next->prev = node->prev;
443 }
444
445 if (node == g_browser_list)
446 {
447 g_browser_list = node->next;
448 }
449 grub_free(node);
450 }
451 else
452 {
453 break;
454 }
455 }
456 }
457 g_browser_list = NULL;
458
459 grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)mbuf.buf, mbuf.pos);
460 grub_script_execute_sourcecode(cfgfile);
461
462 ventoy_browser_mbuf_free(&mbuf);
463 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
464 }
465
466 grub_err_t ventoy_cmd_browser_disk(grub_extcmd_context_t ctxt, int argc, char **args)
467 {
468 char cfgfile[64];
469 browser_mbuf mbuf;
470
471 (void)ctxt;
472 (void)argc;
473 (void)args;
474
475 if (!ventoy_browser_mbuf_alloc(&mbuf))
476 {
477 return 1;
478 }
479
480 g_vtoy_dev = grub_env_get("vtoydev");
481
482 browser_ssprintf(&mbuf, "menuentry \"%-10s [Return]\" --class=\"vtoyret\" VTOY_RET {\n "
483 " echo 'return ...' \n}\n", "<--");
484
485 grub_disk_dev_iterate(ventoy_browser_iterate_disk, &mbuf);
486
487 grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)mbuf.buf, mbuf.pos);
488 grub_script_execute_sourcecode(cfgfile);
489
490 ventoy_browser_mbuf_free(&mbuf);
491 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
492 }
493