1 /******************************************************************************
2 * partresize.c ---- ventoy part resize util
4 * Copyright (c) 2021, 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/>.
27 #include <sys/types.h>
29 #include <sys/ioctl.h>
31 #include <sys/types.h>
33 #include <fat_filelib.h>
36 static int g_disk_fd
= 0;
37 static UINT64 g_disk_offset
= 0;
38 static GUID g_ZeroGuid
= {0};
39 static GUID g_WindowsDataPartGuid
= { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
41 static int vtoy_disk_read(uint32 sector
, uint8
*buffer
, uint32 sector_count
)
43 UINT64 offset
= sector
* 512ULL;
45 lseek(g_disk_fd
, g_disk_offset
+ offset
, SEEK_SET
);
46 read(g_disk_fd
, buffer
, sector_count
* 512);
51 static int vtoy_disk_write(uint32 sector
, uint8
*buffer
, uint32 sector_count
)
53 UINT64 offset
= sector
* 512ULL;
55 lseek(g_disk_fd
, g_disk_offset
+ offset
, SEEK_SET
);
56 write(g_disk_fd
, buffer
, sector_count
* 512);
62 static int gpt_check(const char *disk
)
66 VTOY_GPT_INFO
*pGPT
= NULL
;
68 fd
= open(disk
, O_RDONLY
);
71 printf("Failed to open %s\n", disk
);
75 pGPT
= malloc(sizeof(VTOY_GPT_INFO
));
80 memset(pGPT
, 0, sizeof(VTOY_GPT_INFO
));
82 read(fd
, pGPT
, sizeof(VTOY_GPT_INFO
));
84 if (pGPT
->MBR
.PartTbl
[0].FsFlag
== 0xEE && memcmp(pGPT
->Head
.Signature
, "EFI PART", 8) == 0)
95 static int part_check(const char *disk
)
105 UINT64 NextPartStart
;
106 UINT64 DiskSizeInBytes
;
107 VTOY_GPT_INFO
*pGPT
= NULL
;
109 DiskSizeInBytes
= get_disk_size_in_byte(disk
);
110 if (DiskSizeInBytes
== 0)
112 printf("Failed to get disk size of %s\n", disk
);
116 fd
= open(disk
, O_RDONLY
);
119 printf("Failed to open %s\n", disk
);
123 pGPT
= malloc(sizeof(VTOY_GPT_INFO
));
128 memset(pGPT
, 0, sizeof(VTOY_GPT_INFO
));
130 read(fd
, pGPT
, sizeof(VTOY_GPT_INFO
));
132 if (pGPT
->MBR
.PartTbl
[0].FsFlag
== 0xEE && memcmp(pGPT
->Head
.Signature
, "EFI PART", 8) == 0)
143 PART_TABLE
*PartTbl
= pGPT
->MBR
.PartTbl
;
145 for (Count
= 0, i
= 0; i
< 4; i
++)
147 if (PartTbl
[i
].SectorCount
> 0)
149 printf("MBR Part%d SectorStart:%u SectorCount:%u\n", i
+ 1, PartTbl
[i
].StartSectorId
, PartTbl
[i
].SectorCount
);
154 //We must have a free partition table for VTOYEFI partition
157 printf("###[FAIL] 4 MBR partition tables are all used.\n");
161 if (PartTbl
[0].SectorCount
> 0)
163 Part1Start
= PartTbl
[0].StartSectorId
;
164 Part1End
= PartTbl
[0].SectorCount
+ Part1Start
;
168 printf("###[FAIL] MBR Partition 1 is invalid\n");
173 NextPartStart
= DiskSizeInBytes
/ 512ULL;
174 for (i
= 1; i
< 4; i
++)
176 if (PartTbl
[i
].SectorCount
> 0 && NextPartStart
> PartTbl
[i
].StartSectorId
)
179 NextPartStart
= PartTbl
[i
].StartSectorId
;
183 NextPartStart
*= 512ULL;
184 printf("DiskSize:%llu NextPartStart:%llu(LBA:%llu) Index:%d\n",
185 DiskSizeInBytes
, NextPartStart
, NextPartStart
/ 512ULL, Index
);
189 VTOY_GPT_PART_TBL
*PartTbl
= pGPT
->PartTbl
;
191 for (Count
= 0, i
= 0; i
< 128; i
++)
193 if (memcmp(&(PartTbl
[i
].PartGuid
), &g_ZeroGuid
, sizeof(GUID
)))
195 printf("GPT Part%d StartLBA:%llu LastLBA:%llu\n", i
+ 1, PartTbl
[i
].StartLBA
, PartTbl
[i
].LastLBA
);
202 printf("###[FAIL] 128 GPT partition tables are all used.\n");
206 if (memcmp(&(PartTbl
[0].PartGuid
), &g_ZeroGuid
, sizeof(GUID
)))
208 Part1Start
= PartTbl
[0].StartLBA
;
209 Part1End
= PartTbl
[0].LastLBA
+ 1;
213 printf("###[FAIL] GPT Partition 1 is invalid\n");
218 NextPartStart
= (pGPT
->Head
.PartAreaEndLBA
+ 1);
219 for (i
= 1; i
< 128; i
++)
221 if (memcmp(&(PartTbl
[i
].PartGuid
), &g_ZeroGuid
, sizeof(GUID
)) && NextPartStart
> PartTbl
[i
].StartLBA
)
224 NextPartStart
= PartTbl
[i
].StartLBA
;
228 NextPartStart
*= 512ULL;
229 printf("DiskSize:%llu NextPartStart:%llu(LBA:%llu) Index:%d\n",
230 DiskSizeInBytes
, NextPartStart
, NextPartStart
/ 512ULL, Index
);
233 printf("Valid partition table (%s): Valid partition count:%d\n", (PartStyle
== 0) ? "MBR" : "GPT", Count
);
235 //Partition 1 MUST start at 1MB
236 Part1Start
*= 512ULL;
239 printf("Partition 1 start at: %llu %lluKB, end:%llu, NextPartStart:%llu\n",
240 Part1Start
, Part1Start
/ 1024, Part1End
, NextPartStart
);
241 if (Part1Start
!= SIZE_1MB
)
243 printf("###[FAIL] Partition 1 is not start at 1MB\n");
248 //If we have free space after partition 1
249 if (NextPartStart
- Part1End
>= VENTOY_EFI_PART_SIZE
)
251 printf("Free space after partition 1 (%llu) is enough for VTOYEFI part\n", NextPartStart
- Part1End
);
254 else if (NextPartStart
== Part1End
)
256 printf("There is no free space after partition 1\n");
261 printf("The free space after partition 1 is not enough\n");
271 static int secureboot_proc(char *disk
, UINT64 part2start
)
276 char *filebuf
= NULL
;
279 fd
= open(disk
, O_RDWR
);
282 printf("Failed to open %s\n", disk
);
287 g_disk_offset
= part2start
* 512ULL;
291 if (0 == fl_attach_media(vtoy_disk_read
, vtoy_disk_write
))
293 file
= fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
294 printf("Open ventoy efi file %p\n", file
);
297 fl_fseek(file
, 0, SEEK_END
);
298 size
= (int)fl_ftell(file
);
299 fl_fseek(file
, 0, SEEK_SET
);
301 printf("ventoy x64 efi file size %d ...\n", size
);
303 filebuf
= (char *)malloc(size
);
306 fl_fread(filebuf
, 1, size
, file
);
311 fl_remove("/EFI/BOOT/BOOTX64.EFI");
312 fl_remove("/EFI/BOOT/grubx64.efi");
313 fl_remove("/EFI/BOOT/grubx64_real.efi");
314 fl_remove("/EFI/BOOT/MokManager.efi");
315 fl_remove("/EFI/BOOT/mmx64.efi");
316 fl_remove("/ENROLL_THIS_KEY_IN_MOKMANAGER.cer");
318 file
= fl_fopen("/EFI/BOOT/BOOTX64.EFI", "wb");
319 printf("Open bootx64 efi file %p\n", file
);
324 fl_fwrite(filebuf
, 1, size
, file
);
337 file
= fl_fopen("/EFI/BOOT/grubia32_real.efi", "rb");
338 printf("Open ventoy ia32 efi file %p\n", file
);
341 fl_fseek(file
, 0, SEEK_END
);
342 size
= (int)fl_ftell(file
);
343 fl_fseek(file
, 0, SEEK_SET
);
345 printf("ventoy efi file size %d ...\n", size
);
347 filebuf
= (char *)malloc(size
);
350 fl_fread(filebuf
, 1, size
, file
);
355 fl_remove("/EFI/BOOT/BOOTIA32.EFI");
356 fl_remove("/EFI/BOOT/grubia32.efi");
357 fl_remove("/EFI/BOOT/grubia32_real.efi");
358 fl_remove("/EFI/BOOT/mmia32.efi");
360 file
= fl_fopen("/EFI/BOOT/BOOTIA32.EFI", "wb");
361 printf("Open bootia32 efi file %p\n", file
);
366 fl_fwrite(filebuf
, 1, size
, file
);
391 static int VentoyFillMBRLocation(UINT64 DiskSizeInBytes
, UINT32 StartSectorId
, UINT32 SectorCount
, PART_TABLE
*Table
)
400 while (nHead
!= 0 && (DiskSizeInBytes
/ 512 / nSector
/ nHead
) > 1024)
402 nHead
= (UINT8
)nHead
* 2;
410 Cylinder
= StartSectorId
/ nSector
/ nHead
;
411 Head
= StartSectorId
/ nSector
% nHead
;
412 Sector
= StartSectorId
% nSector
+ 1;
414 Table
->StartHead
= Head
;
415 Table
->StartSector
= Sector
;
416 Table
->StartCylinder
= Cylinder
;
418 EndSectorId
= StartSectorId
+ SectorCount
- 1;
419 Cylinder
= EndSectorId
/ nSector
/ nHead
;
420 Head
= EndSectorId
/ nSector
% nHead
;
421 Sector
= EndSectorId
% nSector
+ 1;
423 Table
->EndHead
= Head
;
424 Table
->EndSector
= Sector
;
425 Table
->EndCylinder
= Cylinder
;
427 Table
->StartSectorId
= StartSectorId
;
428 Table
->SectorCount
= SectorCount
;
434 static int WriteDataToPhyDisk(int fd
, UINT64 offset
, void *buffer
, int len
)
439 newseek
= lseek(fd
, offset
, SEEK_SET
);
440 if (newseek
!= offset
)
442 printf("Failed to lseek %llu %lld %d\n", offset
, (long long)newseek
, errno
);
446 wrlen
= write(fd
, buffer
, len
);
447 if ((int)wrlen
!= len
)
449 printf("Failed to write %d %d %d\n", len
, (int)wrlen
, errno
);
456 static int VentoyFillBackupGptHead(VTOY_GPT_INFO
*pInfo
, VTOY_GPT_HDR
*pHead
)
461 memcpy(pHead
, &pInfo
->Head
, sizeof(VTOY_GPT_HDR
));
463 LBA
= pHead
->EfiStartLBA
;
464 BackupLBA
= pHead
->EfiBackupLBA
;
466 pHead
->EfiStartLBA
= BackupLBA
;
467 pHead
->EfiBackupLBA
= LBA
;
468 pHead
->PartTblStartLBA
= BackupLBA
+ 1 - 33;
471 pHead
->Crc
= VtoyCrc32(pHead
, pHead
->Length
);
476 static int update_part_table(char *disk
, UINT64 part2start
)
484 UINT64 DiskSizeInBytes
;
485 VTOY_GPT_INFO
*pGPT
= NULL
;
486 VTOY_GPT_HDR
*pBack
= NULL
;
488 DiskSizeInBytes
= get_disk_size_in_byte(disk
);
489 if (DiskSizeInBytes
== 0)
491 printf("Failed to get disk size of %s\n", disk
);
495 fd
= open(disk
, O_RDWR
);
498 printf("Failed to open %s\n", disk
);
502 pGPT
= malloc(sizeof(VTOY_GPT_INFO
) + sizeof(VTOY_GPT_HDR
));
507 memset(pGPT
, 0, sizeof(VTOY_GPT_INFO
) + sizeof(VTOY_GPT_HDR
));
509 pBack
= (VTOY_GPT_HDR
*)(pGPT
+ 1);
511 len
= read(fd
, pGPT
, sizeof(VTOY_GPT_INFO
));
512 if (len
!= (ssize_t
)sizeof(VTOY_GPT_INFO
))
514 printf("Failed to read partition table %d err:%d\n", (int)len
, errno
);
518 if (pGPT
->MBR
.PartTbl
[0].FsFlag
== 0xEE && memcmp(pGPT
->Head
.Signature
, "EFI PART", 8) == 0)
529 PART_TABLE
*PartTbl
= pGPT
->MBR
.PartTbl
;
531 for (i
= 1; i
< 4; i
++)
533 if (PartTbl
[i
].SectorCount
== 0)
541 printf("###[FAIL] Can not find a free MBR partition table.\n");
545 for (j
= i
- 1; j
> 0; j
--)
547 printf("Move MBR partition table %d --> %d\n", j
+ 1, j
+ 2);
548 memcpy(PartTbl
+ (j
+ 1), PartTbl
+ j
, sizeof(PART_TABLE
));
551 memset(PartTbl
+ 1, 0, sizeof(PART_TABLE
));
552 VentoyFillMBRLocation(DiskSizeInBytes
, (UINT32
)part2start
, VENTOY_EFI_PART_SIZE
/ 512, PartTbl
+ 1);
553 PartTbl
[1].Active
= 0x00;
554 PartTbl
[1].FsFlag
= 0xEF; // EFI System Partition
556 PartTbl
[0].Active
= 0x80; // bootable
557 PartTbl
[0].SectorCount
= (UINT32
)part2start
- 2048;
559 if (!WriteDataToPhyDisk(fd
, 0, &(pGPT
->MBR
), 512))
561 printf("MBR write MBR failed\n");
566 printf("MBR update partition table success.\n");
571 VTOY_GPT_PART_TBL
*PartTbl
= pGPT
->PartTbl
;
573 for (i
= 1; i
< 128; i
++)
575 if (memcmp(&(PartTbl
[i
].PartGuid
), &g_ZeroGuid
, sizeof(GUID
)) == 0)
583 printf("###[FAIL] Can not find a free GPT partition table.\n");
587 for (j
= i
- 1; j
> 0; j
--)
589 printf("Move GPT partition table %d --> %d\n", j
+ 1, j
+ 2);
590 memcpy(PartTbl
+ (j
+ 1), PartTbl
+ j
, sizeof(VTOY_GPT_PART_TBL
));
593 // to fix windows issue
594 memset(PartTbl
+ 1, 0, sizeof(VTOY_GPT_PART_TBL
));
595 memcpy(&(PartTbl
[1].PartType
), &g_WindowsDataPartGuid
, sizeof(GUID
));
596 ventoy_gen_preudo_uuid(&(PartTbl
[1].PartGuid
));
598 PartTbl
[0].LastLBA
= part2start
- 1;
600 PartTbl
[1].StartLBA
= PartTbl
[0].LastLBA
+ 1;
601 PartTbl
[1].LastLBA
= PartTbl
[1].StartLBA
+ VENTOY_EFI_PART_SIZE
/ 512 - 1;
602 PartTbl
[1].Attr
= VENTOY_EFI_PART_ATTR
;
603 PartTbl
[1].Name
[0] = 'V';
604 PartTbl
[1].Name
[1] = 'T';
605 PartTbl
[1].Name
[2] = 'O';
606 PartTbl
[1].Name
[3] = 'Y';
607 PartTbl
[1].Name
[4] = 'E';
608 PartTbl
[1].Name
[5] = 'F';
609 PartTbl
[1].Name
[6] = 'I';
610 PartTbl
[1].Name
[7] = 0;
613 pGPT
->Head
.PartTblCrc
= VtoyCrc32(pGPT
->PartTbl
, sizeof(pGPT
->PartTbl
));
615 pGPT
->Head
.Crc
= VtoyCrc32(&(pGPT
->Head
), pGPT
->Head
.Length
);
617 printf("pGPT->Head.EfiStartLBA=%llu\n", pGPT
->Head
.EfiStartLBA
);
618 printf("pGPT->Head.EfiBackupLBA=%llu\n", pGPT
->Head
.EfiBackupLBA
);
620 VentoyFillBackupGptHead(pGPT
, pBack
);
621 if (!WriteDataToPhyDisk(fd
, pGPT
->Head
.EfiBackupLBA
* 512, pBack
, 512))
623 printf("GPT write backup head failed\n");
627 if (!WriteDataToPhyDisk(fd
, (pGPT
->Head
.EfiBackupLBA
- 32) * 512, pGPT
->PartTbl
, 512 * 32))
629 printf("GPT write backup partition table failed\n");
633 if (!WriteDataToPhyDisk(fd
, 0, pGPT
, 512 * 34))
635 printf("GPT write MBR & Main partition table failed\n");
640 printf("GPT update partition table success.\n");
650 int partresize_main(int argc
, char **argv
)
654 if (argc
!= 3 && argc
!= 4)
656 printf("usage: partresize -c/-f /dev/sdb\n");
660 if (strcmp(argv
[1], "-c") == 0)
662 return part_check(argv
[2]);
664 else if (strcmp(argv
[1], "-s") == 0)
666 sector
= strtoull(argv
[3], NULL
, 10);
667 return secureboot_proc(argv
[2], sector
);
669 else if (strcmp(argv
[1], "-p") == 0)
671 sector
= strtoull(argv
[3], NULL
, 10);
672 return update_part_table(argv
[2], sector
);
674 else if (strcmp(argv
[1], "-t") == 0)
676 return gpt_check(argv
[2]);