1 /******************************************************************************
2 * ventoy_disk.c ---- ventoy disk
3 * Copyright (c) 2021, longpanda <admin@ventoy.net>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 3 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
27 #include <sys/types.h>
28 #include <sys/ioctl.h>
30 #include <sys/types.h>
34 #include <ventoy_define.h>
35 #include <ventoy_disk.h>
36 #include <ventoy_util.h>
37 #include <fat_filelib.h>
40 static int g_fatlib_media_fd
= 0;
41 static uint64_t g_fatlib_media_offset
= 0;
42 ventoy_disk
*g_disk_list
= NULL
;
44 static const char *g_ventoy_dev_type_str
[VTOY_DEVICE_END
] =
46 "unknown", "scsi", "USB", "ide", "dac960",
47 "cpqarray", "file", "ataraid", "i2o",
48 "ubd", "dasd", "viodasd", "sx8", "dm",
49 "xvd", "sd/mmc", "virtblk", "aoe",
50 "md", "loopback", "nvme", "brd", "pmem"
53 static const char * ventoy_get_dev_type_name(ventoy_dev_type type
)
55 return (type
< VTOY_DEVICE_END
) ? g_ventoy_dev_type_str
[type
] : "unknown";
58 static int ventoy_check_blk_major(int major
, const char *type
)
68 fp
= fopen("/proc/devices", "r");
74 len
= (int)strlen(type
);
75 while (fgets(line
, sizeof(line
), fp
))
79 pos
= strchr(line
, ' ');
82 devnum
= (int)strtol(line
, NULL
, 10);
85 if (strncmp(pos
+ 1, type
, len
) == 0)
93 else if (strncmp(line
, "Block devices:", 14) == 0)
103 static int ventoy_get_disk_devnum(const char *name
, int *major
, int* minor
)
107 char devnum
[16] = {0};
109 rc
= ventoy_get_sys_file_line(devnum
, sizeof(devnum
), "/sys/block/%s/dev", name
);
115 pos
= strstr(devnum
, ":");
121 *major
= (int)strtol(devnum
, NULL
, 10);
122 *minor
= (int)strtol(pos
+ 1, NULL
, 10);
127 static ventoy_dev_type
ventoy_get_dev_type(const char *name
, int major
, int minor
)
133 memset(syspath
, 0, sizeof(syspath
));
134 memset(dstpath
, 0, sizeof(dstpath
));
136 scnprintf(syspath
, "/sys/block/%s", name
);
137 rc
= readlink(syspath
, dstpath
, sizeof(dstpath
) - 1);
138 if (rc
> 0 && strstr(dstpath
, "/usb"))
140 return VTOY_DEVICE_USB
;
143 if (SCSI_BLK_MAJOR(major
) && (minor
% 0x10 == 0))
145 return VTOY_DEVICE_SCSI
;
147 else if (IDE_BLK_MAJOR(major
) && (minor
% 0x40 == 0))
149 return VTOY_DEVICE_IDE
;
151 else if (major
== DAC960_MAJOR
&& (minor
% 0x8 == 0))
153 return VTOY_DEVICE_DAC960
;
155 else if (major
== ATARAID_MAJOR
&& (minor
% 0x10 == 0))
157 return VTOY_DEVICE_ATARAID
;
159 else if (major
== AOE_MAJOR
&& (minor
% 0x10 == 0))
161 return VTOY_DEVICE_AOE
;
163 else if (major
== DASD_MAJOR
&& (minor
% 0x4 == 0))
165 return VTOY_DEVICE_DASD
;
167 else if (major
== VIODASD_MAJOR
&& (minor
% 0x8 == 0))
169 return VTOY_DEVICE_VIODASD
;
171 else if (SX8_BLK_MAJOR(major
) && (minor
% 0x20 == 0))
173 return VTOY_DEVICE_SX8
;
175 else if (I2O_BLK_MAJOR(major
) && (minor
% 0x10 == 0))
177 return VTOY_DEVICE_I2O
;
179 else if (CPQARRAY_BLK_MAJOR(major
) && (minor
% 0x10 == 0))
181 return VTOY_DEVICE_CPQARRAY
;
183 else if (UBD_MAJOR
== major
&& (minor
% 0x10 == 0))
185 return VTOY_DEVICE_UBD
;
187 else if (XVD_MAJOR
== major
&& (minor
% 0x10 == 0))
189 return VTOY_DEVICE_XVD
;
191 else if (SDMMC_MAJOR
== major
&& (minor
% 0x8 == 0))
193 return VTOY_DEVICE_SDMMC
;
195 else if (ventoy_check_blk_major(major
, "virtblk"))
197 return VTOY_DEVICE_VIRTBLK
;
199 else if (major
== LOOP_MAJOR
)
201 return VTOY_DEVICE_LOOP
;
203 else if (major
== MD_MAJOR
)
205 return VTOY_DEVICE_MD
;
207 else if (major
== RAM_MAJOR
)
209 return VTOY_DEVICE_RAM
;
211 else if (strstr(name
, "nvme") && ventoy_check_blk_major(major
, "blkext"))
213 return VTOY_DEVICE_NVME
;
215 else if (strstr(name
, "pmem") && ventoy_check_blk_major(major
, "blkext"))
217 return VTOY_DEVICE_PMEM
;
220 return VTOY_DEVICE_END
;
223 static int ventoy_is_possible_blkdev(const char *name
)
231 if (name
[0] == 'r' && name
[1] == 'a' && name
[2] == 'm')
237 if (name
[0] == 'z' && name
[1] == 'r' && name
[2] == 'a' && name
[3] == 'm')
243 if (name
[0] == 'l' && name
[1] == 'o' && name
[2] == 'o' && name
[3] == 'p')
249 if (name
[0] == 'd' && name
[1] == 'm' && name
[2] == '-' && isdigit(name
[3]))
255 if (name
[0] == 's' && name
[1] == 'r' && isdigit(name
[2]))
263 int ventoy_is_disk_4k_native(const char *disk
)
269 char diskpath
[256] = {0};
271 snprintf(diskpath
, sizeof(diskpath
) - 1, "/dev/%s", disk
);
273 fd
= open(diskpath
, O_RDONLY
| O_BINARY
);
276 ioctl(fd
, BLKSSZGET
, &logsector
);
277 ioctl(fd
, BLKPBSZGET
, &physector
);
279 if (logsector
== 4096 && physector
== 4096)
286 vdebug("is 4k native disk <%s> <%d>\n", disk
, rc
);
290 uint64_t ventoy_get_disk_size_in_byte(const char *disk
)
294 unsigned long long size
= 0;
295 char diskpath
[256] = {0};
296 char sizebuf
[64] = {0};
298 // Try 1: get size from sysfs
299 snprintf(diskpath
, sizeof(diskpath
) - 1, "/sys/block/%s/size", disk
);
300 if (access(diskpath
, F_OK
) >= 0)
302 vdebug("get disk size from sysfs for %s\n", disk
);
304 fd
= open(diskpath
, O_RDONLY
| O_BINARY
);
307 read(fd
, sizebuf
, sizeof(sizebuf
));
308 size
= strtoull(sizebuf
, NULL
, 10);
310 return (uint64_t)(size
* 512);
315 vdebug("%s not exist \n", diskpath
);
318 // Try 2: get size from ioctl
319 snprintf(diskpath
, sizeof(diskpath
) - 1, "/dev/%s", disk
);
320 fd
= open(diskpath
, O_RDONLY
);
323 vdebug("get disk size from ioctl for %s\n", disk
);
324 rc
= ioctl(fd
, BLKGETSIZE64
, &size
);
328 vdebug("failed to ioctl %d\n", rc
);
334 vdebug("failed to open %s %d\n", diskpath
, errno
);
337 vdebug("disk %s size %llu bytes\n", disk
, size
);
341 int ventoy_get_disk_vendor(const char *name
, char *vendorbuf
, int bufsize
)
343 return ventoy_get_sys_file_line(vendorbuf
, bufsize
, "/sys/block/%s/device/vendor", name
);
346 int ventoy_get_disk_model(const char *name
, char *modelbuf
, int bufsize
)
348 return ventoy_get_sys_file_line(modelbuf
, bufsize
, "/sys/block/%s/device/model", name
);
351 static int fatlib_media_sector_read(uint32 sector
, uint8
*buffer
, uint32 sector_count
)
353 lseek(g_fatlib_media_fd
, (sector
+ g_fatlib_media_offset
) * 512ULL, SEEK_SET
);
354 read(g_fatlib_media_fd
, buffer
, sector_count
* 512);
359 static int fatlib_is_secure_boot_enable(void)
363 flfile
= fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
366 vlog("/EFI/BOOT/grubx64_real.efi find, secure boot in enabled\n");
372 vlog("/EFI/BOOT/grubx64_real.efi not exist\n");
378 static int fatlib_get_ventoy_version(char *verbuf
, int bufsize
)
387 flfile
= fl_fopen("/grub/grub.cfg", "rb");
390 fl_fseek(flfile
, 0, SEEK_END
);
391 size
= (int)fl_ftell(flfile
);
393 fl_fseek(flfile
, 0, SEEK_SET
);
395 buf
= malloc(size
+ 1);
398 fl_fread(buf
, 1, size
, flfile
);
401 pos
= strstr(buf
, "VENTOY_VERSION=");
404 pos
+= strlen("VENTOY_VERSION=");
411 while (*end
!= 0 && *end
!= '"' && *end
!= '\r' && *end
!= '\n')
418 snprintf(verbuf
, bufsize
- 1, "%s", pos
);
428 vdebug("No grub.cfg found\n");
434 int ventoy_get_vtoy_data(ventoy_disk
*info
, int *ppartstyle
)
442 uint64_t part1_start_sector
;
443 uint64_t part1_sector_count
;
444 uint64_t part2_start_sector
;
445 uint64_t part2_sector_count
;
446 uint64_t preserved_space
;
448 disk_ventoy_data
*vtoy
= NULL
;
449 VTOY_GPT_INFO
*gpt
= NULL
;
451 vtoy
= &(info
->vtoydata
);
452 gpt
= &(vtoy
->gptinfo
);
453 memset(vtoy
, 0, sizeof(disk_ventoy_data
));
455 vdebug("ventoy_get_vtoy_data %s\n", info
->disk_path
);
457 if (info
->size_in_byte
< (2 * VTOYEFI_PART_BYTES
))
459 vdebug("disk %s is too small %llu\n", info
->disk_path
, (_ull
)info
->size_in_byte
);
463 fd
= open(info
->disk_path
, O_RDONLY
| O_BINARY
);
466 vdebug("failed to open %s %d\n", info
->disk_path
, errno
);
470 len
= (int)read(fd
, &(vtoy
->gptinfo
), sizeof(VTOY_GPT_INFO
));
471 if (len
!= sizeof(VTOY_GPT_INFO
))
473 vdebug("failed to read %s %d\n", info
->disk_path
, errno
);
477 if (gpt
->MBR
.Byte55
!= 0x55 || gpt
->MBR
.ByteAA
!= 0xAA)
479 vdebug("Invalid mbr magic 0x%x 0x%x\n", gpt
->MBR
.Byte55
, gpt
->MBR
.ByteAA
);
483 if (gpt
->MBR
.PartTbl
[0].FsFlag
== 0xEE && strncmp(gpt
->Head
.Signature
, "EFI PART", 8) == 0)
485 part_style
= GPT_PART_STYLE
;
488 *ppartstyle
= part_style
;
491 if (gpt
->PartTbl
[0].StartLBA
== 0 || gpt
->PartTbl
[1].StartLBA
== 0)
493 vdebug("NO ventoy efi part layout <%llu %llu>\n",
494 (_ull
)gpt
->PartTbl
[0].StartLBA
,
495 (_ull
)gpt
->PartTbl
[1].StartLBA
);
499 for (i
= 0; i
< 36; i
++)
501 name
[i
] = (char)(gpt
->PartTbl
[1].Name
[i
]);
503 if (strcmp(name
, "VTOYEFI"))
505 vdebug("Invalid efi part2 name <%s>\n", name
);
509 part1_start_sector
= gpt
->PartTbl
[0].StartLBA
;
510 part1_sector_count
= gpt
->PartTbl
[0].LastLBA
- part1_start_sector
+ 1;
511 part2_start_sector
= gpt
->PartTbl
[1].StartLBA
;
512 part2_sector_count
= gpt
->PartTbl
[1].LastLBA
- part2_start_sector
+ 1;
514 preserved_space
= info
->size_in_byte
- (part2_start_sector
+ part2_sector_count
+ 33) * 512;
518 part_style
= MBR_PART_STYLE
;
521 *ppartstyle
= part_style
;
524 part1_start_sector
= gpt
->MBR
.PartTbl
[0].StartSectorId
;
525 part1_sector_count
= gpt
->MBR
.PartTbl
[0].SectorCount
;
526 part2_start_sector
= gpt
->MBR
.PartTbl
[1].StartSectorId
;
527 part2_sector_count
= gpt
->MBR
.PartTbl
[1].SectorCount
;
529 preserved_space
= info
->size_in_byte
- (part2_start_sector
+ part2_sector_count
) * 512;
532 if (part1_start_sector
!= VTOYIMG_PART_START_SECTOR
||
533 part2_sector_count
!= VTOYEFI_PART_SECTORS
||
534 (part1_start_sector
+ part1_sector_count
) != part2_start_sector
)
536 vdebug("Not valid ventoy partition layout [%llu %llu] [%llu %llu]\n",
537 part1_start_sector
, part1_sector_count
, part2_start_sector
, part2_sector_count
);
541 vdebug("ventoy partition layout check OK: [%llu %llu] [%llu %llu]\n",
542 part1_start_sector
, part1_sector_count
, part2_start_sector
, part2_sector_count
);
544 vtoy
->ventoy_valid
= 1;
546 vdebug("now check secure boot for %s ...\n", info
->disk_path
);
548 g_fatlib_media_fd
= fd
;
549 g_fatlib_media_offset
= part2_start_sector
;
552 if (0 == fl_attach_media(fatlib_media_sector_read
, NULL
))
554 ret
= fatlib_get_ventoy_version(vtoy
->ventoy_ver
, sizeof(vtoy
->ventoy_ver
));
555 if (ret
== 0 && vtoy
->ventoy_ver
[0])
557 vtoy
->secure_boot_flag
= fatlib_is_secure_boot_enable();
561 vdebug("fatlib_get_ventoy_version failed %d\n", ret
);
566 vdebug("fl_attach_media failed\n");
570 g_fatlib_media_fd
= -1;
571 g_fatlib_media_offset
= 0;
573 if (vtoy
->ventoy_ver
[0] == 0)
575 vtoy
->ventoy_ver
[0] = '?';
578 if (0 == vtoy
->ventoy_valid
)
583 lseek(fd
, 2040 * 512, SEEK_SET
);
584 read(fd
, vtoy
->rsvdata
, sizeof(vtoy
->rsvdata
));
586 vtoy
->preserved_space
= preserved_space
;
587 vtoy
->partition_style
= part_style
;
588 vtoy
->part2_start_sector
= part2_start_sector
;
592 vtoy_safe_close_fd(fd
);
596 int ventoy_get_disk_info(const char *name
, ventoy_disk
*info
)
598 char vendor
[64] = {0};
599 char model
[128] = {0};
601 vdebug("get disk info %s\n", name
);
603 strlcpy(info
->disk_name
, name
);
604 scnprintf(info
->disk_path
, "/dev/%s", name
);
606 if (strstr(name
, "nvme") || strstr(name
, "mmc") || strstr(name
, "nbd"))
608 scnprintf(info
->part1_name
, "%sp1", name
);
609 scnprintf(info
->part1_path
, "/dev/%sp1", name
);
610 scnprintf(info
->part2_name
, "%sp2", name
);
611 scnprintf(info
->part2_path
, "/dev/%sp2", name
);
615 scnprintf(info
->part1_name
, "%s1", name
);
616 scnprintf(info
->part1_path
, "/dev/%s1", name
);
617 scnprintf(info
->part2_name
, "%s2", name
);
618 scnprintf(info
->part2_path
, "/dev/%s2", name
);
621 info
->is4kn
= ventoy_is_disk_4k_native(name
);
622 info
->size_in_byte
= ventoy_get_disk_size_in_byte(name
);
624 ventoy_get_disk_devnum(name
, &info
->major
, &info
->minor
);
625 info
->type
= ventoy_get_dev_type(name
, info
->major
, info
->minor
);
626 ventoy_get_disk_vendor(name
, vendor
, sizeof(vendor
));
627 ventoy_get_disk_model(name
, model
, sizeof(model
));
629 scnprintf(info
->human_readable_size
, "%llu GB", (_ull
)ventoy_get_human_readable_gb(info
->size_in_byte
));
630 scnprintf(info
->disk_model
, "%s %s (%s)", vendor
, model
, ventoy_get_dev_type_name(info
->type
));
632 ventoy_get_vtoy_data(info
, &(info
->partstyle
));
634 vdebug("disk:<%s %d:%d> model:<%s> size:%llu (%s)\n",
635 info
->disk_path
, info
->major
, info
->minor
, info
->disk_model
, info
->size_in_byte
, info
->human_readable_size
);
637 if (info
->vtoydata
.ventoy_valid
)
639 vdebug("%s Ventoy:<%s> %s secureboot:%d preserve:%llu\n", info
->disk_path
, info
->vtoydata
.ventoy_ver
,
640 info
->vtoydata
.partition_style
== MBR_PART_STYLE
? "MBR" : "GPT",
641 info
->vtoydata
.secure_boot_flag
, (_ull
)(info
->vtoydata
.preserved_space
));
645 vdebug("%s NO Ventoy detected\n", info
->disk_path
);
651 static int ventoy_disk_compare(const ventoy_disk
*disk1
, const ventoy_disk
*disk2
)
653 if (disk1
->type
== VTOY_DEVICE_USB
&& disk2
->type
== VTOY_DEVICE_USB
)
655 return strcmp(disk1
->disk_name
, disk2
->disk_name
);
657 else if (disk1
->type
== VTOY_DEVICE_USB
)
661 else if (disk2
->type
== VTOY_DEVICE_USB
)
667 return strcmp(disk1
->disk_name
, disk2
->disk_name
);
671 static int ventoy_disk_sort(void)
676 tmp
= malloc(sizeof(ventoy_disk
));
682 for (i
= 0; i
< g_disk_num
; i
++)
683 for (j
= i
+ 1; j
< g_disk_num
; j
++)
685 if (ventoy_disk_compare(g_disk_list
+ i
, g_disk_list
+ j
) > 0)
687 memcpy(tmp
, g_disk_list
+ i
, sizeof(ventoy_disk
));
688 memcpy(g_disk_list
+ i
, g_disk_list
+ j
, sizeof(ventoy_disk
));
689 memcpy(g_disk_list
+ j
, tmp
, sizeof(ventoy_disk
));
697 int ventoy_disk_enumerate_all(void)
701 struct dirent
* p
= NULL
;
703 vdebug("ventoy_disk_enumerate_all\n");
705 dir
= opendir("/sys/block");
708 vlog("Failed to open /sys/block %d\n", errno
);
712 while (((p
= readdir(dir
)) != NULL
) && (g_disk_num
< MAX_DISK_NUM
))
714 if (ventoy_is_possible_blkdev(p
->d_name
))
716 memset(g_disk_list
+ g_disk_num
, 0, sizeof(ventoy_disk
));
717 if (0 == ventoy_get_disk_info(p
->d_name
, g_disk_list
+ g_disk_num
))
730 void ventoy_disk_dump(ventoy_disk
*cur
)
732 if (cur
->vtoydata
.ventoy_valid
)
734 vdebug("%s [%s] %s\tVentoy: %s %s secureboot:%d preserve:%llu\n",
735 cur
->disk_path
, cur
->human_readable_size
, cur
->disk_model
,
736 cur
->vtoydata
.ventoy_ver
, cur
->vtoydata
.partition_style
== MBR_PART_STYLE
? "MBR" : "GPT",
737 cur
->vtoydata
.secure_boot_flag
, (_ull
)(cur
->vtoydata
.preserved_space
));
741 vdebug("%s [%s] %s\tVentoy: NA\n", cur
->disk_path
, cur
->human_readable_size
, cur
->disk_model
);
745 void ventoy_disk_dump_all(void)
749 vdebug("============= DISK DUMP ============\n");
750 for (i
= 0; i
< g_disk_num
; i
++)
752 ventoy_disk_dump(g_disk_list
+ i
);
756 int ventoy_disk_install(ventoy_disk
*disk
, void *efipartimg
)
762 int ventoy_disk_init(void)
764 g_disk_list
= malloc(sizeof(ventoy_disk
) * MAX_DISK_NUM
);
766 ventoy_disk_enumerate_all();
767 ventoy_disk_dump_all();
772 void ventoy_disk_exit(void)
774 check_free(g_disk_list
);