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