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 uint64_t ventoy_get_disk_size_in_byte(const char *disk
)
267 unsigned long long size
= 0;
268 char diskpath
[256] = {0};
269 char sizebuf
[64] = {0};
271 // Try 1: get size from sysfs
272 snprintf(diskpath
, sizeof(diskpath
) - 1, "/sys/block/%s/size", disk
);
273 if (access(diskpath
, F_OK
) >= 0)
275 vdebug("get disk size from sysfs for %s\n", disk
);
277 fd
= open(diskpath
, O_RDONLY
| O_BINARY
);
280 read(fd
, sizebuf
, sizeof(sizebuf
));
281 size
= strtoull(sizebuf
, NULL
, 10);
283 return (uint64_t)(size
* 512);
288 vdebug("%s not exist \n", diskpath
);
291 // Try 2: get size from ioctl
292 snprintf(diskpath
, sizeof(diskpath
) - 1, "/dev/%s", disk
);
293 fd
= open(diskpath
, O_RDONLY
);
296 vdebug("get disk size from ioctl for %s\n", disk
);
297 rc
= ioctl(fd
, BLKGETSIZE64
, &size
);
301 vdebug("failed to ioctl %d\n", rc
);
307 vdebug("failed to open %s %d\n", diskpath
, errno
);
310 vdebug("disk %s size %llu bytes\n", disk
, size
);
314 int ventoy_get_disk_vendor(const char *name
, char *vendorbuf
, int bufsize
)
316 return ventoy_get_sys_file_line(vendorbuf
, bufsize
, "/sys/block/%s/device/vendor", name
);
319 int ventoy_get_disk_model(const char *name
, char *modelbuf
, int bufsize
)
321 return ventoy_get_sys_file_line(modelbuf
, bufsize
, "/sys/block/%s/device/model", name
);
324 static int fatlib_media_sector_read(uint32 sector
, uint8
*buffer
, uint32 sector_count
)
326 lseek(g_fatlib_media_fd
, (sector
+ g_fatlib_media_offset
) * 512ULL, SEEK_SET
);
327 read(g_fatlib_media_fd
, buffer
, sector_count
* 512);
332 static int fatlib_is_secure_boot_enable(void)
336 flfile
= fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
339 vlog("/EFI/BOOT/grubx64_real.efi find, secure boot in enabled\n");
345 vlog("/EFI/BOOT/grubx64_real.efi not exist\n");
351 static int fatlib_get_ventoy_version(char *verbuf
, int bufsize
)
360 flfile
= fl_fopen("/grub/grub.cfg", "rb");
363 fl_fseek(flfile
, 0, SEEK_END
);
364 size
= (int)fl_ftell(flfile
);
366 fl_fseek(flfile
, 0, SEEK_SET
);
368 buf
= malloc(size
+ 1);
371 fl_fread(buf
, 1, size
, flfile
);
374 pos
= strstr(buf
, "VENTOY_VERSION=");
377 pos
+= strlen("VENTOY_VERSION=");
384 while (*end
!= 0 && *end
!= '"' && *end
!= '\r' && *end
!= '\n')
391 snprintf(verbuf
, bufsize
- 1, "%s", pos
);
401 vdebug("No grub.cfg found\n");
407 int ventoy_get_vtoy_data(ventoy_disk
*info
, int *ppartstyle
)
415 uint64_t part1_start_sector
;
416 uint64_t part1_sector_count
;
417 uint64_t part2_start_sector
;
418 uint64_t part2_sector_count
;
419 uint64_t preserved_space
;
421 disk_ventoy_data
*vtoy
= NULL
;
422 VTOY_GPT_INFO
*gpt
= NULL
;
424 vtoy
= &(info
->vtoydata
);
425 gpt
= &(vtoy
->gptinfo
);
426 memset(vtoy
, 0, sizeof(disk_ventoy_data
));
428 vdebug("ventoy_get_vtoy_data %s\n", info
->disk_path
);
430 if (info
->size_in_byte
< (2 * VTOYEFI_PART_BYTES
))
432 vdebug("disk %s is too small %llu\n", info
->disk_path
, (_ull
)info
->size_in_byte
);
436 fd
= open(info
->disk_path
, O_RDONLY
| O_BINARY
);
439 vdebug("failed to open %s %d\n", info
->disk_path
, errno
);
443 len
= (int)read(fd
, &(vtoy
->gptinfo
), sizeof(VTOY_GPT_INFO
));
444 if (len
!= sizeof(VTOY_GPT_INFO
))
446 vdebug("failed to read %s %d\n", info
->disk_path
, errno
);
450 if (gpt
->MBR
.Byte55
!= 0x55 || gpt
->MBR
.ByteAA
!= 0xAA)
452 vdebug("Invalid mbr magic 0x%x 0x%x\n", gpt
->MBR
.Byte55
, gpt
->MBR
.ByteAA
);
456 if (gpt
->MBR
.PartTbl
[0].FsFlag
== 0xEE && strncmp(gpt
->Head
.Signature
, "EFI PART", 8) == 0)
458 part_style
= GPT_PART_STYLE
;
461 *ppartstyle
= part_style
;
464 if (gpt
->PartTbl
[0].StartLBA
== 0 || gpt
->PartTbl
[1].StartLBA
== 0)
466 vdebug("NO ventoy efi part layout <%llu %llu>\n",
467 (_ull
)gpt
->PartTbl
[0].StartLBA
,
468 (_ull
)gpt
->PartTbl
[1].StartLBA
);
472 for (i
= 0; i
< 36; i
++)
474 name
[i
] = (char)(gpt
->PartTbl
[1].Name
[i
]);
476 if (strcmp(name
, "VTOYEFI"))
478 vdebug("Invalid efi part2 name <%s>\n", name
);
482 part1_start_sector
= gpt
->PartTbl
[0].StartLBA
;
483 part1_sector_count
= gpt
->PartTbl
[0].LastLBA
- part1_start_sector
+ 1;
484 part2_start_sector
= gpt
->PartTbl
[1].StartLBA
;
485 part2_sector_count
= gpt
->PartTbl
[1].LastLBA
- part2_start_sector
+ 1;
487 preserved_space
= info
->size_in_byte
- (part2_start_sector
+ part2_sector_count
+ 33) * 512;
491 part_style
= MBR_PART_STYLE
;
494 *ppartstyle
= part_style
;
497 part1_start_sector
= gpt
->MBR
.PartTbl
[0].StartSectorId
;
498 part1_sector_count
= gpt
->MBR
.PartTbl
[0].SectorCount
;
499 part2_start_sector
= gpt
->MBR
.PartTbl
[1].StartSectorId
;
500 part2_sector_count
= gpt
->MBR
.PartTbl
[1].SectorCount
;
502 preserved_space
= info
->size_in_byte
- (part2_start_sector
+ part2_sector_count
) * 512;
505 if (part1_start_sector
!= VTOYIMG_PART_START_SECTOR
||
506 part2_sector_count
!= VTOYEFI_PART_SECTORS
||
507 (part1_start_sector
+ part1_sector_count
) != part2_start_sector
)
509 vdebug("Not valid ventoy partition layout [%llu %llu] [%llu %llu]\n",
510 part1_start_sector
, part1_sector_count
, part2_start_sector
, part2_sector_count
);
514 vdebug("ventoy partition layout check OK: [%llu %llu] [%llu %llu]\n",
515 part1_start_sector
, part1_sector_count
, part2_start_sector
, part2_sector_count
);
517 vdebug("now check secure boot for %s ...\n", info
->disk_path
);
519 g_fatlib_media_fd
= fd
;
520 g_fatlib_media_offset
= part2_start_sector
;
523 if (0 == fl_attach_media(fatlib_media_sector_read
, NULL
))
525 ret
= fatlib_get_ventoy_version(vtoy
->ventoy_ver
, sizeof(vtoy
->ventoy_ver
));
526 if (ret
== 0 && vtoy
->ventoy_ver
[0])
528 vtoy
->secure_boot_flag
= fatlib_is_secure_boot_enable();
529 vtoy
->ventoy_valid
= 1;
533 vdebug("fatlib_get_ventoy_version failed %d\n", ret
);
538 vdebug("fl_attach_media failed\n");
542 g_fatlib_media_fd
= -1;
543 g_fatlib_media_offset
= 0;
545 if (0 == vtoy
->ventoy_valid
)
550 lseek(fd
, 2040 * 512, SEEK_SET
);
551 read(fd
, vtoy
->rsvdata
, sizeof(vtoy
->rsvdata
));
553 vtoy
->preserved_space
= preserved_space
;
554 vtoy
->partition_style
= part_style
;
555 vtoy
->part2_start_sector
= part2_start_sector
;
559 vtoy_safe_close_fd(fd
);
563 int ventoy_get_disk_info(const char *name
, ventoy_disk
*info
)
565 char vendor
[64] = {0};
566 char model
[128] = {0};
568 vdebug("get disk info %s\n", name
);
570 strlcpy(info
->disk_name
, name
);
571 scnprintf(info
->disk_path
, "/dev/%s", name
);
573 if (strstr(name
, "nvme") || strstr(name
, "mmc") || strstr(name
, "nbd"))
575 scnprintf(info
->part1_name
, "%sp1", name
);
576 scnprintf(info
->part1_path
, "/dev/%sp1", name
);
577 scnprintf(info
->part2_name
, "%sp2", name
);
578 scnprintf(info
->part2_path
, "/dev/%sp2", name
);
582 scnprintf(info
->part1_name
, "%s1", name
);
583 scnprintf(info
->part1_path
, "/dev/%s1", name
);
584 scnprintf(info
->part2_name
, "%s2", name
);
585 scnprintf(info
->part2_path
, "/dev/%s2", name
);
588 info
->size_in_byte
= ventoy_get_disk_size_in_byte(name
);
590 ventoy_get_disk_devnum(name
, &info
->major
, &info
->minor
);
591 info
->type
= ventoy_get_dev_type(name
, info
->major
, info
->minor
);
592 ventoy_get_disk_vendor(name
, vendor
, sizeof(vendor
));
593 ventoy_get_disk_model(name
, model
, sizeof(model
));
595 scnprintf(info
->human_readable_size
, "%llu GB", (_ull
)ventoy_get_human_readable_gb(info
->size_in_byte
));
596 scnprintf(info
->disk_model
, "%s %s (%s)", vendor
, model
, ventoy_get_dev_type_name(info
->type
));
598 ventoy_get_vtoy_data(info
, &(info
->partstyle
));
600 vdebug("disk:<%s %d:%d> model:<%s> size:%llu (%s)\n",
601 info
->disk_path
, info
->major
, info
->minor
, info
->disk_model
, info
->size_in_byte
, info
->human_readable_size
);
603 if (info
->vtoydata
.ventoy_valid
)
605 vdebug("%s Ventoy:<%s> %s secureboot:%d preserve:%llu\n", info
->disk_path
, info
->vtoydata
.ventoy_ver
,
606 info
->vtoydata
.partition_style
== MBR_PART_STYLE
? "MBR" : "GPT",
607 info
->vtoydata
.secure_boot_flag
, (_ull
)(info
->vtoydata
.preserved_space
));
611 vdebug("%s NO Ventoy detected\n", info
->disk_path
);
617 static int ventoy_disk_compare(const ventoy_disk
*disk1
, const ventoy_disk
*disk2
)
619 if (disk1
->type
== VTOY_DEVICE_USB
&& disk2
->type
== VTOY_DEVICE_USB
)
621 return strcmp(disk1
->disk_name
, disk2
->disk_name
);
623 else if (disk1
->type
== VTOY_DEVICE_USB
)
627 else if (disk2
->type
== VTOY_DEVICE_USB
)
633 return strcmp(disk1
->disk_name
, disk2
->disk_name
);
637 static int ventoy_disk_sort(void)
642 tmp
= malloc(sizeof(ventoy_disk
));
648 for (i
= 0; i
< g_disk_num
; i
++)
649 for (j
= i
+ 1; j
< g_disk_num
; j
++)
651 if (ventoy_disk_compare(g_disk_list
+ i
, g_disk_list
+ j
) > 0)
653 memcpy(tmp
, g_disk_list
+ i
, sizeof(ventoy_disk
));
654 memcpy(g_disk_list
+ i
, g_disk_list
+ j
, sizeof(ventoy_disk
));
655 memcpy(g_disk_list
+ j
, tmp
, sizeof(ventoy_disk
));
663 int ventoy_disk_enumerate_all(void)
667 struct dirent
* p
= NULL
;
669 vdebug("ventoy_disk_enumerate_all\n");
671 dir
= opendir("/sys/block");
674 vlog("Failed to open /sys/block %d\n", errno
);
678 while (((p
= readdir(dir
)) != NULL
) && (g_disk_num
< MAX_DISK_NUM
))
680 if (ventoy_is_possible_blkdev(p
->d_name
))
682 memset(g_disk_list
+ g_disk_num
, 0, sizeof(ventoy_disk
));
683 if (0 == ventoy_get_disk_info(p
->d_name
, g_disk_list
+ g_disk_num
))
696 void ventoy_disk_dump(ventoy_disk
*cur
)
698 if (cur
->vtoydata
.ventoy_valid
)
700 vdebug("%s [%s] %s\tVentoy: %s %s secureboot:%d preserve:%llu\n",
701 cur
->disk_path
, cur
->human_readable_size
, cur
->disk_model
,
702 cur
->vtoydata
.ventoy_ver
, cur
->vtoydata
.partition_style
== MBR_PART_STYLE
? "MBR" : "GPT",
703 cur
->vtoydata
.secure_boot_flag
, (_ull
)(cur
->vtoydata
.preserved_space
));
707 vdebug("%s [%s] %s\tVentoy: NA\n", cur
->disk_path
, cur
->human_readable_size
, cur
->disk_model
);
711 void ventoy_disk_dump_all(void)
715 vdebug("============= DISK DUMP ============\n");
716 for (i
= 0; i
< g_disk_num
; i
++)
718 ventoy_disk_dump(g_disk_list
+ i
);
722 int ventoy_disk_install(ventoy_disk
*disk
, void *efipartimg
)
728 int ventoy_disk_init(void)
730 g_disk_list
= malloc(sizeof(ventoy_disk
) * MAX_DISK_NUM
);
732 ventoy_disk_enumerate_all();
733 ventoy_disk_dump_all();
738 void ventoy_disk_exit(void)
740 check_free(g_disk_list
);