4 * Copyright 2021 Sylvain BERTRAND <sylvain.bertrand@legeek.net>
5 * LICENSE: GNU AFFERO GPLV3
6 * (NO WARANTY OF ANY KIND AND BLAHBLAHBLAH)
9 /* quick and dirty, minimal, line input oriented uefi gpt partition creator */
13 * - implement ANSI terminal pagination (listing of entries and types)
14 * - utf8 to utf16 converter for names (no iconv)
29 * how to get pertinent device information from sysfs for gpt partitioning
31 * for a /dev/sdX block device:
32 * /sys/block/sdX/size = size of the device in blocks of 512 bytes (hardcoded)
33 * /sys/block/sdX/queue/logical_block_size = size in bytes of a logical block
34 * which is the size used by lba (Logical Block Access) offsets used
36 * /sys/block/sdX/queue/physical_block_size = size in bytes of a physical block
38 /*{{{ global preprocessor definitions */
41 #define utf16 uint16_t
42 #define X64_UTF8_BYTES_MAX (sizeof("18446744073709551615") - 1)
44 #define strtou64 strtoul
51 #define ARRAY_N(x) (sizeof(x) / sizeof(x[0]))
52 /*}}} global preprocessor definitions -- end */
53 /*{{{ nyangpt namespace */
54 #define blk_dev nyangpt_blk_dev
55 #define build_data_from_previous_gpt nyangpt_data_from_previous_gpt
56 #define change_disk_guid_prompt nyangpt_change_disk_guid_prompt
57 #define colors_on nyangpt_colors_on
58 #define delete_entry_prompt nyangpt_delete_entry_prompt
59 #define edit_entry nyangpt_edit_entry
60 #define edit_entry_attrs_prompt nyangpt_edit_entry_attrs_prompt
61 #define edit_entry_first_lba_prompt nyangpt_edit_entry_first_lba_prompt
62 #define edit_entry_last_lba_prompt nyangpt_edit_entry_last_lba_prompt
63 #define edit_entry_prompt nyangpt_edit_entry_prompt
64 #define edit_entry_to_entries nyangpt_edit_entry_to_entries
65 #define edit_entry_type_prompt nyangpt_edit_entry_type_prompt
66 #define edit_entry_uniq_guid_prompt nyangpt_edit_entry_uniq_guid_prompt
67 #define entries nyangpt_entries
68 #define entries_lbas_n nyangpt_entries_lbas_n
69 #define entries_load nyangpt_entries_load
70 #define entries_n nyangpt_entries_n
71 #define entries_reset nyangpt_entries_reset
72 #define entries_serialized_array nyangpt_entries_serialized_array
73 #define entries_serialized_array_crc32 nyangpt_entries_serialized_array_crc32
74 #define entries_serialized_array_gen nyangpt_entries_serialized_array_gen
75 #define entries_serialized_array_gen_entry nyangpt_entries_serialized_array_gen_entry
76 #define entries_serialized_array_primary_write nyangpt_entries_serialized_array_primary_write
77 #define entries_serialized_array_secondary_write nyangpt_entries_serialized_array_secondary_write
78 #define entries_show nyangpt_entries_show
79 #define entry_bytes_n nyangpt_entry_bytes_n
80 #define entry_delete nyangpt_entry_delete
81 #define entry_is_used nyangpt_entry_is_used
82 #define entry_show nyangpt_entry_show
83 #define entry_t nyangpt_entry_t
84 #define error_pf nyangpt_error_pf
85 #define first_lba_default_select nyangpt_first_lba_default_select
86 #define gpt_write nyangpt_gpt_write
87 #define guid_is_zero nyangpt_guid_is_zero
88 #define guid_t nyangpt_guid_t
89 #define guid_randomize nyangpt_guid_randomize
90 #define guid_read nyangpt_guid_read
91 #define guid_read_from_input nyangph_guid_read_from_input
92 #define guid_write nyangpt_guid_write
93 #define hdr_load nyangpt_hdr_load
94 #define hdr_show nyangpt_hdr_show
95 #define hdr_primary_serialized nyangpt_hdr_primary_serialized
96 #define hdr_primary_serialized_gen nyangpt_hdr_primary_serialized_gen
97 #define hdr_primary_serialized_write nyangpt_hdr_primary_serialized_write
98 #define hdr_secondary_serialized nyangpt_hdr_secondary_serialized
99 #define hdr_secondary_serialized_gen nyangpt_hdr_secondary_serialized_gen
100 #define hdr_secondary_serialized_write nyangpt_hdr_secondary_serialized_write
101 #define hdr_t nyangpt_hdr_t
102 #define hdr_validate nyangpt_hdr_validate
103 #define hdrs nyangpt_hdrs
104 #define hdrs_usable_lbas_compute nyangpt_hdrs_usable_lbas_compute
105 #define init_once nyangpt_init_once
106 #define input_line nyangpt_input_line
107 #define input_line_consume nyangpt_input_line_consume
108 #define input_line_loop nyangpt_input_line_loop
109 #define input_state nyangpt_input_state
110 #define last_lba_default_select nyangpt_last_lba_default_select
111 #define le_crc32_for_byte nyangpt_le_crc32_for_byte
112 #define le_crc32_tbl nyangpt_le_crc32_tbl
113 #define le_crc32_tbl_gen nyangpt_le_crc32_tbl_gen
114 #define le_crc32_update nyangpt_le_crc32_update
115 #define load_previous_gpt nyangpt_load_previous_gpt
116 #define main_menu_prompt nyangpt_main_menu_prompt
117 #define main_menu_show nyangpt_main_menu_show
118 #define options_parse nyangpt_options_parse
119 #define out_bold_pf nyangpt_out_bold_pf
120 #define out_guid nyangpt_out_guid
121 #define out_pf nyangpt_out_pf
122 #define pf nyangpt_pf
123 #define previous_gpt_load nyangpt_previous_gpt_load
124 #define protective_mbr nyangpt_protective_mbr
125 #define protective_mbr_gen nyangpt_protective_mbr_gen
126 #define protective_mbr_write nyangpt_protective_mbr_write
127 #define read_full nyangpt_read_full
128 #define state_change_disk_guid nyangpt_state_change_disk_guid
129 #define state_delete_entry nyangpt_state_delete_entry
130 #define state_edit_entry nyangpt_state_edit_entry
131 #define state_edit_entry_substate_attrs nyangpt_state_edit_entry_substate_attrs
132 #define state_edit_entry_substate_first_lba nyangpt_state_edit_entry_substate_first_lba
133 #define state_edit_entry_substate_last_lba nyangpt_state_edit_entry_substate_last_lba
134 #define state_edit_entry_substate_type nyangpt_state_edit_entry_substate_type
135 #define state_edit_entry_substate_uniq_quid nyangpt_state_edit_entry_substate_uniq_quid
136 #define state_main_menu nyangpt_state_main_menu
137 #define sysfs_infos_get nyangpt_sysfs_infos_get
138 #define type_guids_lookup_name nyangpt_type_guids_lookup_name
139 #define types nyangpt_types
140 #define types_show nyangpt_types_show
141 #define usage nyangpt_usage
142 #define utf16_strdup nyangpt_utf16_strdup
143 #define warning_pf nyangpt_warning_pf
144 #define write_full nyangpt_write_full
145 /*----------------------------------------------------------------------------*/
146 #define nyangpt_main main
147 /*}}} nyangpt namespace -- end -----------------------------------------------*/
148 /*{{{ crc32b little endian */
149 /* stolen and cosmetized, not validated on big endian */
150 /* http://home.thep.lu.se/~bjorn/crc/ */
151 STATIC u32
le_crc32_for_byte(u32 r
)
159 r
= (r
& 1 ? 0 : (u32
)0xedb88320) ^ r
>> 1;
162 return r
^ (u32
)0xff000000;
164 STATIC u32 le_crc32_tbl
[0x100];
165 STATIC
void le_crc32_tbl_gen(void)
173 le_crc32_tbl
[i
] = le_crc32_for_byte(i
);
177 STATIC
void le_crc32_update(void *data
, u64 bytes_n
, u32
* crc
)
185 *crc
= le_crc32_tbl
[(u8
)*crc
^ ((u8
*)data
)[i
]] ^ *crc
>> 8;
189 /*}}} crc32b little endian -- end *********************************************/
198 /* serialized offsets */
204 /*----------------------------------------------------------------------------*/
208 u64 read_from_lba
; /* _should_ be the same as the lba field */
211 /* fields -- start */
217 u64 alternate_hdr_lba
;
218 /* first usable lba */
219 /* last usable lba */
220 struct guid_t disk_guid
;
224 u32 entries_le_crc32
;
230 /*----------------------------------------------------------------------------*/
239 #define ENTRIES_ARRAY_MIN_BYTES_N 16384 /* specs: minimum to book on the disk */
240 #define ENTRY_BYTES_N 0x80 /* specs */
241 /*}}} types -- end */
242 STATIC
bool colors_on
;
243 STATIC
bool load_previous_gpt
;
244 /*----------------------------------------------------------------------------*/
248 u64 logical_blk_bytes_n
;
249 u64 physical_blk_bytes_n
;
253 /*----------------------------------------------------------------------------*/
254 STATIC u8
*protective_mbr
;
255 /* serialized offsets */
256 #define BOOT_SIGNATURE_0X55 0x1fe
257 #define BOOT_SIGNATURE_0XAA 0x1ff
259 /*----------------------------------------------------------------------------*/
260 STATIC
struct { /* there are 2 headers, each must fit in a logical block */
261 u64 first_usable_lba
;
263 struct guid_t disk_guid
;
264 u32 bytes_n
; /* the mitigated hdr size */
267 struct hdr_t primary
;
268 struct hdr_t secondary
;
271 #define HDR_SIGNATURE "EFI PART"
272 #define HDR_SIGNATURE_BYTES_N 8
273 #define HDR_REVISION 0x00010000 /* 1.0 */
274 #define HDR_BYTES_N 0x5c /* specs: 92 bytes */
275 STATIC u8
*hdr_primary_serialized
;
276 STATIC u8
*hdr_secondary_serialized
;
277 /*----------------------------------------------------------------------------*/
278 STATIC
struct entry_t
*entries
;
279 STATIC u32 entries_n
;
280 STATIC u32 entry_bytes_n
;
281 STATIC u32 entries_lbas_n
;
282 STATIC u8
*entries_serialized_array
;
283 STATIC u32 entries_serialized_array_crc32
;
284 /*----------------------------------------------------------------------------*/
285 STATIC utf8
*pf(utf8
*fmt
,...)
292 r
= vsnprintf(0, 0, fmt
, ap
);
295 r_str
= malloc(r
+ 1); /* we want a terminating 0 */
298 vsnprintf(r_str
, r
+ 1, fmt
, ap
); /* has room for the terminating 0 */
302 #define BOLD_RED if (colors_on) dprintf(1,"\x1b[38;2;255;0;0m\x1b[1m")
303 #define BOLD_ORANGE if (colors_on) dprintf(1,"\x1b[38;2;255;140;0m\x1b[1m")
304 #define BOLD if (colors_on) dprintf(1,"\x1b[1m");
305 #define RESTORE if (colors_on) dprintf(1, "\x1b[m")
306 STATIC
void error_pf(utf8
*fmt
,...)
311 dprintf(1, "ERROR:");
313 vdprintf(1, fmt
, ap
);
318 STATIC
void warning_pf(utf8
*fmt
,...)
323 dprintf(1, "WARNING:");
325 vdprintf(1, fmt
, ap
);
329 STATIC
void out_pf(utf8
*fmt
,...)
334 vdprintf(1, fmt
, ap
);
337 STATIC
void out_bold_pf(utf8
*fmt
,...)
343 vdprintf(1, fmt
, ap
);
347 STATIC
bool read_full(int fd
, void *dest
, u64 bytes_n
)
356 r
= read(fd
, dest
+ read_bytes_n
,
357 (size_t)(bytes_n
- read_bytes_n
));
363 read_bytes_n
+= (u64
)r
;
364 if (read_bytes_n
== bytes_n
)
369 STATIC
bool write_full(int fd
, void *src
, u64 bytes_n
)
379 r
= write(blk_dev
.fd
, src
+ written_bytes_n
,
380 (size_t)(bytes_n
- written_bytes_n
));
386 written_bytes_n
+= (u64
)r
;
387 if (written_bytes_n
== bytes_n
)
392 STATIC utf16
*utf16_strdup(utf16
*start
)
405 bytes_n
= (size_t)((end
- start
) * sizeof(utf16
));
406 dst
= calloc(1,bytes_n
);
407 memcpy(dst
, start
, bytes_n
);
409 /* brain damaged mixed-endian guid */
410 STATIC
void guid_write(void *dest
, struct guid_t
*src
)
419 *p32
= htole32(src
->blk_0
); /* little endian */
422 *p16
= htole16(src
->blk_1
); /* little endian */
425 *p16
= htole16(src
->blk_2
); /* little endian */
428 *p16
= htobe16(src
->blk_3
); /* big endian */
430 d
[0] = src
->blk_4
[0];
431 d
[1] = src
->blk_4
[1];
432 d
[2] = src
->blk_4
[2];
433 d
[3] = src
->blk_4
[3];
434 d
[4] = src
->blk_4
[4];
435 d
[5] = src
->blk_4
[5];
437 /* brain damaged mixed-endian guid */
438 STATIC
void guid_read(struct guid_t
*dest
, void *src
)
445 dest
->blk_0
= le32toh(*p32
);
448 dest
->blk_1
= le16toh(*p16
);
451 dest
->blk_2
= le16toh(*p16
);
454 dest
->blk_3
= be16toh(*p16
);
456 memcpy(dest
->blk_4
, src
, 6);
458 STATIC
void sysfs_infos_get(void)
462 utf8 val_str
[X64_UTF8_BYTES_MAX
+ 1]; /* 0 terminating char */
465 utf8
*logical_blk_bytes_n_path
;
466 utf8
*physical_blk_bytes_n_path
;
468 blk_dev_name
= strrchr(blk_dev
.path
, '/');
470 sz_512_n_path
= pf("/sys/block/%s/size", blk_dev_name
);
471 out_pf("%s:reading %s\n", blk_dev
.path
, sz_512_n_path
);
472 fd
= open(sz_512_n_path
, O_RDONLY
);
474 error_pf("%s:unable to open %s\n", blk_dev
.path
, sz_512_n_path
);
476 /* reads are supposed to be atomic from sysfs... I guess */
477 r
= read(fd
, val_str
, sizeof(val_str
));
478 val_str
[r
- 1] = 0; /* remove the terminating '\n' */
479 blk_dev
.sz_512_n
= strtou64(val_str
, 0, 10);
480 out_bold_pf("%s:size is %"PRIu64
" blocks of 512 bytes\n", blk_dev
.path
, blk_dev
.sz_512_n
);
483 logical_blk_bytes_n_path
= pf("/sys/block/%s/queue/logical_block_size", blk_dev_name
);
484 out_pf("%s:reading %s\n", blk_dev
.path
, logical_blk_bytes_n_path
);
485 fd
= open(logical_blk_bytes_n_path
, O_RDONLY
);
487 error_pf("%s:unable to open %s\n", blk_dev
.path
, logical_blk_bytes_n_path
);
488 free(logical_blk_bytes_n_path
);
489 /* reads are supposed to be atomic from sysfs... I guess */
490 r
= read(fd
, val_str
, sizeof(val_str
));
491 val_str
[r
- 1] = 0; /* remove the terminating '\n' */
492 blk_dev
.logical_blk_bytes_n
= strtou64(val_str
, 0, 10);
493 out_bold_pf("%s:logical block size is %"PRIu64
" bytes\n", blk_dev
.path
, blk_dev
.logical_blk_bytes_n
);
496 physical_blk_bytes_n_path
= pf("/sys/block/%s/queue/physical_block_size", blk_dev_name
);
497 out_pf("%s:reading %s\n", blk_dev
.path
, physical_blk_bytes_n_path
);
498 fd
= open(physical_blk_bytes_n_path
, O_RDONLY
);
500 error_pf("%s:unable to open %s\n", blk_dev
.path
, physical_blk_bytes_n_path
);
501 free(physical_blk_bytes_n_path
);
502 /* reads are supposed to be atomic from sysfs... I guess */
503 r
= read(fd
, val_str
, sizeof(val_str
));
504 val_str
[r
- 1] = 0; /* remove the terminating '\n' */
505 blk_dev
.physical_blk_bytes_n
= strtou64(val_str
, 0, 10);
506 out_bold_pf("%s:physical block size is %"PRIu64
" bytes\n", blk_dev
.path
, blk_dev
.physical_blk_bytes_n
);
509 STATIC
void protective_mbr_gen(void)
511 u32
*le32_whole_dev_logical_blks_n
;
512 u64 whole_dev_bytes_n
;
513 u64 whole_dev_logical_blks_n
;
515 if (blk_dev
.logical_blk_bytes_n
< 512)
516 error_pf("%s: something is wrong, the logical block size is %"PRIu64
", below 512/0x200 bytes", blk_dev
.path
, blk_dev
.logical_blk_bytes_n
);
517 protective_mbr
= calloc(1, blk_dev
.logical_blk_bytes_n
);
519 protective_mbr
[PART_0
+ 0x02] = 0x02; /* first CHS */
520 protective_mbr
[PART_0
+ 0x04] = 0xee; /* partition type */
521 protective_mbr
[PART_0
+ 0x05] = 0xff; /* last head */
522 protective_mbr
[PART_0
+ 0x06] = 0xff; /* last cylinder MSBs + last sector */
523 protective_mbr
[PART_0
+ 0x07] = 0xff; /* last cylinder LSBs */
524 protective_mbr
[PART_0
+ 0x08] = 0x1; /* little endian */
526 whole_dev_bytes_n
= blk_dev
.sz_512_n
* 512;
527 whole_dev_logical_blks_n
= whole_dev_bytes_n
/ blk_dev
.logical_blk_bytes_n
;
528 /* cap to max, remove the MBR in LBA 0 */
529 if (whole_dev_logical_blks_n
> 0x100000000)
530 whole_dev_logical_blks_n
= 0xffffffff;
531 le32_whole_dev_logical_blks_n
= (u32
*)&protective_mbr
[PART_0
+ 0x0c];
532 *le32_whole_dev_logical_blks_n
= htole32( /* remove mbr */
533 (u32
)whole_dev_logical_blks_n
- 1);
535 protective_mbr
[BOOT_SIGNATURE_0X55
] = 0x55;
536 protective_mbr
[BOOT_SIGNATURE_0XAA
] = 0xaa;
538 STATIC
void protective_mbr_write(void)
542 r0
= lseek(blk_dev
.fd
, 0, SEEK_SET
);
544 error_pf("%s:unable to reach the start of the device\n", blk_dev
.path
);
545 if (!write_full(blk_dev
.fd
, protective_mbr
,
546 blk_dev
.logical_blk_bytes_n
))
547 error_pf("%s:unable to write the protective master boot record (mbr), device mbr is now probably corrupted\n", blk_dev
.path
);
548 out_bold_pf("%s:protective master boot record (mbr) written, %"PRIu64
" bytes\n", blk_dev
.path
, blk_dev
.logical_blk_bytes_n
);
550 STATIC
void entries_serialized_array_gen_entry(u32 entry_idx
)
555 entry
= entries_serialized_array
+ entry_bytes_n
* entry_idx
;
556 guid_write(&entry
[0x00], &entries
[entry_idx
].type
);
557 guid_write(&entry
[0x10], &entries
[entry_idx
].uniq
);
558 p64
= (u64
*)&entry
[0x20];
559 *p64
= htole64(entries
[entry_idx
].first
);
560 p64
= (u64
*)&entry
[0x28];
561 *p64
= htole64(entries
[entry_idx
].last
);
563 STATIC
void entries_serialized_array_gen(void)
567 entries_serialized_array
= calloc(1, entries_lbas_n
568 * blk_dev
.logical_blk_bytes_n
);
571 if (entry_idx
== entries_n
)
573 entries_serialized_array_gen_entry(entry_idx
);
577 STATIC
void hdr_primary_serialized_gen(void)
584 hdr_primary_serialized
= calloc(1, blk_dev
.logical_blk_bytes_n
);
586 memcpy(hdr_primary_serialized
, HDR_SIGNATURE
, 8);
588 p32
= (u32
*)&hdr_primary_serialized
[0x08];
589 *p32
= htole32(HDR_REVISION
);
591 p32
=(u32
*)&hdr_primary_serialized
[0x0c];
592 *p32
= htole32(hdrs
.bytes_n
);
594 /* the CRC32 will go there, 0 for its calculation */
596 p64
= (u64
*)&hdr_primary_serialized
[0x18];
597 *p64
= htole64(0x00000001); /* lba of this hdr */
599 p64
= (u64
*)&hdr_primary_serialized
[0x20];
600 *p64
= htole64(blk_dev
.last_lba
); /* the hdr at the end */
602 p64
= (u64
*)&hdr_primary_serialized
[0x28];
603 *p64
= htole64(hdrs
.first_usable_lba
);
605 p64
= (u64
*)&hdr_primary_serialized
[0x30];
606 *p64
= htole64(hdrs
.last_usable_lba
);
608 guid_write(&hdr_primary_serialized
[0x38], &hdrs
.disk_guid
);
610 p64
= (u64
*)&hdr_primary_serialized
[0x48];
611 *p64
= htole64(2); /* skip mbr and hdr */
613 p32
= (u32
*)&hdr_primary_serialized
[0x50];
614 *p32
= htole32(entries_n
);
616 p32
= (u32
*)&hdr_primary_serialized
[0x54];
617 *p32
= htole32(entry_bytes_n
);
619 p32
= (u32
*)&hdr_primary_serialized
[0x58];
620 *p32
= htole32(entries_serialized_array_crc32
);
622 /* crc32 on exactly the header size */
624 le_crc32_update(hdr_primary_serialized
, hdrs
.bytes_n
, &le_crc32
);
625 p32
= (u32
*)&hdr_primary_serialized
[0x10];
628 STATIC
void hdr_secondary_serialized_gen(void)
635 hdr_secondary_serialized
= calloc(1, blk_dev
.logical_blk_bytes_n
);
637 memcpy(hdr_secondary_serialized
, HDR_SIGNATURE
, 8);
639 p32
= (u32
*)&hdr_secondary_serialized
[0x08];
640 *p32
= htole32(HDR_REVISION
);
642 p32
=(u32
*)&hdr_secondary_serialized
[0x0c];
643 *p32
= htole32(hdrs
.bytes_n
);
645 /* the CRC32 will go there, 0 for its calculation */
647 p64
= (u64
*)&hdr_secondary_serialized
[0x18];
648 *p64
= htole64(blk_dev
.last_lba
); /* this hdr */
650 p64
= (u64
*)&hdr_secondary_serialized
[0x20];
651 *p64
= htole64(1); /* the hdr at the beginning */
653 p64
= (u64
*)&hdr_secondary_serialized
[0x28];
654 *p64
= htole64(hdrs
.first_usable_lba
);
656 p64
= (u64
*)&hdr_secondary_serialized
[0x30];
657 *p64
= htole64(hdrs
.last_usable_lba
);
659 guid_write(&hdr_secondary_serialized
[0x38], &hdrs
.disk_guid
);
661 p64
= (u64
*)&hdr_secondary_serialized
[0x48];
662 *p64
= htole64(blk_dev
.last_lba
- entries_lbas_n
);
664 p32
= (u32
*)&hdr_secondary_serialized
[0x50];
665 *p32
= htole32(entries_n
);
667 p32
= (u32
*)&hdr_secondary_serialized
[0x54];
668 *p32
= htole32(entry_bytes_n
);
670 p32
= (u32
*)&hdr_secondary_serialized
[0x58];
671 *p32
= htole32(entries_serialized_array_crc32
);
673 /* crc32 on exactly the header size */
675 le_crc32_update(hdr_secondary_serialized
, hdrs
.bytes_n
, &le_crc32
);
676 p32
= (u32
*)&hdr_secondary_serialized
[0x10];
679 STATIC
void hdr_primary_serialized_write(void)
683 u64 bytes_to_write_n
;
686 start
= (off_t
)blk_dev
.logical_blk_bytes_n
;
687 r
= lseek(blk_dev
.fd
, start
, SEEK_SET
);
689 error_pf("%s:unable to reach the first lba of the device\n", blk_dev
.path
);
690 bytes_to_write_n
= (size_t)(blk_dev
.logical_blk_bytes_n
);
691 if (!write_full(blk_dev
.fd
, hdr_primary_serialized
, bytes_to_write_n
))
692 error_pf("%s:unable to write the primary GPT header, block device is now probably corrupted\n", blk_dev
.path
);
693 out_bold_pf("%s:primary GPT header written, %"PRIu64
" bytes\n", blk_dev
.path
, bytes_to_write_n
);
695 STATIC
void hdr_secondary_serialized_write(void)
699 u64 bytes_to_write_n
;
701 start
= (off_t
)(blk_dev
.last_lba
* blk_dev
.logical_blk_bytes_n
);
702 r
= lseek(blk_dev
.fd
, start
, SEEK_SET
);
704 error_pf("%s:unable to reach the lba of the secondary header\n", blk_dev
.path
);
705 bytes_to_write_n
= blk_dev
.logical_blk_bytes_n
;
706 if (!write_full(blk_dev
.fd
, hdr_secondary_serialized
, bytes_to_write_n
))
707 error_pf("%s:unable to write the secondary GPT header, block device is now probably corrupted\n", blk_dev
.path
);
708 out_bold_pf("%s:secondary GPT header written, %"PRIu64
" bytes\n", blk_dev
.path
, bytes_to_write_n
);
710 STATIC
void entries_serialized_array_primary_write(void)
714 u64 bytes_to_write_n
;
716 /* skip the mbr and the primary hdr */
717 start
= (off_t
)(blk_dev
.logical_blk_bytes_n
* 2);
718 r
= lseek(blk_dev
.fd
, start
, SEEK_SET
);
720 error_pf("%s:unable to reach the first lba for the primary entries\n", blk_dev
.path
);
721 bytes_to_write_n
= blk_dev
.logical_blk_bytes_n
* entries_lbas_n
;
722 if (!write_full(blk_dev
.fd
, entries_serialized_array
, bytes_to_write_n
))
723 error_pf("%s:unable to write the primary entries, block device is now probably corrupted\n", blk_dev
.path
);
724 out_bold_pf("%s:primary entries written, %"PRIu64
" bytes\n", blk_dev
.path
, bytes_to_write_n
);
726 STATIC
void entries_serialized_array_secondary_write(void)
730 u64 bytes_to_write_n
;
732 /* skip the secondary hdr, offset arithmetic */
733 start
= (off_t
)(blk_dev
.logical_blk_bytes_n
* (blk_dev
.last_lba
735 r
= lseek(blk_dev
.fd
, start
, SEEK_SET
);
737 error_pf("%s:unable to reach the first lba for the secondary entries\n", blk_dev
.path
);
738 bytes_to_write_n
= blk_dev
.logical_blk_bytes_n
* entries_lbas_n
;
739 if (!write_full(blk_dev
.fd
, entries_serialized_array
, bytes_to_write_n
))
740 error_pf("%s:unable to write the secondary entries, block device is now probably corrupted\n", blk_dev
.path
);
741 out_bold_pf("%s:secondary entries written, %"PRIu64
" bytes\n", blk_dev
.path
, bytes_to_write_n
);
743 STATIC
void hdr_load(struct hdr_t
*hdr
)
748 lba_off
= (off_t
)(blk_dev
.logical_blk_bytes_n
* hdr
->read_from_lba
);
750 hdr
->data
= calloc(1, blk_dev
.logical_blk_bytes_n
);
751 r
= lseek(blk_dev
.fd
, lba_off
, SEEK_SET
);
753 warning_pf("%s:unable to seek the device to the %s GPT header\n", blk_dev
.path
, hdr
->str
);
756 if (!read_full(blk_dev
.fd
, hdr
->data
, blk_dev
.logical_blk_bytes_n
)) {
757 warning_pf("%s:unable to load the %s GPT header logical block\n", blk_dev
.path
, hdr
->str
);
762 STATIC
void hdr_validate(struct hdr_t
*hdr
)
770 if (memcmp(hdr
->data
, HDR_SIGNATURE
, HDR_SIGNATURE_BYTES_N
) != 0) {
771 warning_pf("%s:the %s GPT header has the wrong signature\n", blk_dev
.path
, hdr
->str
);
774 out_bold_pf("%s:found, %s, GPT header\n", blk_dev
.path
, hdr
->str
);
775 p32
= (u32
*)&hdr
->data
[0x10];
776 hdr
->le_crc32
= *p32
;
777 /* must 0 the crc32 to perform our crc32 */
779 p32
= (u32
*)&hdr
->data
[0x0c];
780 hdr
->bytes_n
= le32toh(*p32
);
782 le_crc32_update(hdr
->data
, hdr
->bytes_n
, &le_crc32
);
783 if (le_crc32
!= hdr
->le_crc32
) {
784 warning_pf("%s:the %s GPT header has a bad crc32\n", blk_dev
.path
, hdr
->str
);
787 p64
= (u64
*)&hdr
->data
[0x18];
788 hdr
->lba
= le64toh(*p64
);
789 p64
= (u64
*)&hdr
->data
[0x20];
790 hdr
->alternate_hdr_lba
= le64toh(*p64
);
791 guid_read(&hdr
->disk_guid
, &hdr
->data
[0x38]);
792 p64
= (u64
*)&hdr
->data
[0x48];
793 hdr
->entries_lba
= le64toh(*p64
);
794 p32
= (u32
*)&hdr
->data
[0x50];
795 hdr
->entries_n
= le32toh(*p32
);
796 p32
= (u32
*)&hdr
->data
[0x54];
797 hdr
->entry_bytes_n
= le32toh(*p32
);
798 p32
= (u32
*)&hdr
->data
[0x58];
799 hdr
->entries_le_crc32
= *p32
;
807 STATIC
void entries_load(struct hdr_t
*hdr
)
811 u32 hdr_entries_bytes_n
;
814 entries_off
= hdr
->entries_lba
* blk_dev
.logical_blk_bytes_n
;
815 r
= lseek(blk_dev
.fd
, entries_off
, SEEK_SET
);
816 if (r
!= entries_off
) {
817 warning_pf("%s:unable to seek the device to the, %s, GPT header\n", blk_dev
.path
, hdr
->str
);
820 hdr_entries_bytes_n
= hdr
->entry_bytes_n
* hdr
->entries_n
;
821 hdr
->entries
= calloc(1, (size_t)(hdr_entries_bytes_n
));
822 if(!read_full(blk_dev
.fd
, hdr
->entries
, hdr_entries_bytes_n
)) {
823 warning_pf("%s:unable to read the, %s, entries\n", blk_dev
.path
, hdr
->str
);
827 le_crc32_update(hdr
->entries
, hdr_entries_bytes_n
, &le_crc32
);
828 if (le_crc32
!= hdr
->entries_le_crc32
) {
829 warning_pf("%s:%s entries have a bad crc32\n", blk_dev
.path
, hdr
->str
);
832 hdr
->entries_valid
= true;
837 STATIC
void build_data_from_previous_gpt(void)
843 /* first, lookup for valid data */
844 if (hdrs
.previous
.primary
.valid
&& hdrs
.previous
.primary
.entries_valid
)
845 hdr
= &hdrs
.previous
.primary
;
846 else if (hdrs
.previous
.secondary
.valid
847 && hdrs
.previous
.secondary
.entries_valid
)
848 hdr
= &hdrs
.previous
.secondary
;
850 return; /* no valid data to build from */
852 memcpy(&hdrs
.disk_guid
, &hdr
->disk_guid
, sizeof(hdrs
.disk_guid
));
853 hdrs
.bytes_n
= hdr
->bytes_n
;
855 entries
= calloc(hdr
->entries_n
, hdr
->entry_bytes_n
);
856 entries_n
= hdr
->entries_n
;
857 entry_bytes_n
= hdr
->entry_bytes_n
;
858 entries_bytes_n
= hdr
->entries_n
* hdr
->entry_bytes_n
;
859 entries_lbas_n
= entries_bytes_n
/ blk_dev
.logical_blk_bytes_n
860 + ((entries_bytes_n
% blk_dev
.logical_blk_bytes_n
) == 0
865 struct entry_t
*dst_entry
;
868 if (entry_idx
== entries_n
)
870 src_entry
= hdr
->entries
+ entry_idx
* hdr
->entry_bytes_n
;
871 dst_entry
= &entries
[entry_idx
];
873 guid_read(&dst_entry
->type
, src_entry
);
874 guid_read(&dst_entry
->uniq
, &src_entry
[0x10]);
875 p64
= (u64
*)&src_entry
[0x20];
876 dst_entry
->first
= le64toh(*p64
);
877 p64
= (u64
*)&src_entry
[0x28];
878 dst_entry
->last
= le64toh(*p64
);
879 p64
= (u64
*)&src_entry
[0x30];
880 dst_entry
->attrs
= le64toh(*p64
);
881 dst_entry
->name
= utf16_strdup((utf16
*)&src_entry
[0x38]);
885 STATIC
void previous_gpt_load(void)
887 hdrs
.previous
.primary
.read_from_lba
= 1;
888 hdrs
.previous
.primary
.str
= "primary";
889 hdr_load(&hdrs
.previous
.primary
);
890 hdr_validate(&hdrs
.previous
.primary
);
891 if (hdrs
.previous
.primary
.valid
) {
892 entries_load(&hdrs
.previous
.primary
);
893 if (hdrs
.previous
.primary
.entries_valid
)
895 /* damn! we have a valid header but with invalid entries */
896 hdrs
.previous
.secondary
.read_from_lba
=
897 hdrs
.previous
.primary
.alternate_hdr_lba
;
898 hdrs
.previous
.secondary
.str
= "alternate from primary header";
899 } else { /* arg! try the device last lba */
900 hdrs
.previous
.secondary
.read_from_lba
= blk_dev
.last_lba
;
901 hdrs
.previous
.secondary
.str
= "from device last lba";
904 * here, something went wrong with the primary header. 2 cases:
905 * - the primary header is ok but the entries are not, then we are
906 * trying the alternate header from the primary header data.
907 * - the primary header is nok, we are try to look for a secondary
908 * header from the last lba of the device.
910 hdr_load(&hdrs
.previous
.secondary
);
911 hdr_validate(&hdrs
.previous
.secondary
);
912 if (hdrs
.previous
.secondary
.valid
)
913 entries_load(&hdrs
.previous
.secondary
);
915 STATIC
void hdrs_usable_lbas_compute(void)
917 /* protective mbr, hdr, entries */
918 hdrs
.first_usable_lba
= 0 + 2 + entries_lbas_n
;
920 hdrs
.last_usable_lba
= blk_dev
.last_lba
- 1 - entries_lbas_n
;
922 STATIC
void entries_reset(void)
928 if (entries_n
!= 0) {
930 loop
{ /* the utf16 names */
933 free(entries
[i
].name
);
938 /* actually 128 entries */
939 entries_n
= ENTRIES_ARRAY_MIN_BYTES_N
/ ENTRY_BYTES_N
;
940 entries
= calloc(entries_n
, ENTRY_BYTES_N
);
941 entry_bytes_n
= ENTRY_BYTES_N
;
948 entries
[i
].name
= utf16_strdup(&zero
);
951 entries_bytes_n
= entries_n
* entry_bytes_n
;
952 entries_lbas_n
= entries_bytes_n
/ blk_dev
.logical_blk_bytes_n
953 + (entries_bytes_n
% entry_bytes_n
== 0 ? 0 : 1);
955 /******************************************************************************/
956 STATIC
void out_guid(struct guid_t
*guid
)
959 out_pf("%08x-%04x-%04x-%04x-", guid
->blk_0
, guid
->blk_1
, guid
->blk_2
,
965 out_pf("%02x", guid
->blk_4
[i
]);
973 [0].guid
.blk_0
= 0xc12a7328,
974 [0].guid
.blk_1
= 0xf81f,
975 [0].guid
.blk_2
= 0x11d2,
976 [0].guid
.blk_3
= 0xba4b,
977 [0].guid
.blk_4
= {0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b},
978 [0].name
= "EFI system",
980 [1].guid
.blk_0
= 0x4f68bce3,
981 [1].guid
.blk_1
= 0xe8cd,
982 [1].guid
.blk_2
= 0x4db1,
983 [1].guid
.blk_3
= 0x96e7,
984 [1].guid
.blk_4
= {0xfb, 0xca, 0xf9, 0x84, 0xb7, 0x09},
985 [1].name
= "linux root x86_64",
987 [2].guid
.blk_0
= 0x0657fd6d,
988 [2].guid
.blk_1
= 0xa4ab,
989 [2].guid
.blk_2
= 0x43c4,
990 [2].guid
.blk_3
= 0x84e5,
991 [2].guid
.blk_4
= {0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f},
992 [2].name
= "linux swap",
994 STATIC
void types_show(void)
1004 out_pf("%"PRIu32
": ", i
+ 1);
1005 out_guid(&types
[i
].guid
);
1006 out_pf(" %s\n", types
[i
].name
);
1010 STATIC utf8
*type_guids_lookup_name(struct guid_t
*guid
)
1020 if (memcmp(guid
, &types
[i
].guid
, sizeof(*guid
)) == 0)
1021 return types
[i
].name
;
1026 STATIC
void entry_show(u32 idx
, struct entry_t
*entry
)
1031 out_pf("%"PRIu32
": %"PRIu64
" %"PRIu64
" ", idx
+ 1, entry
->first
, entry
->last
);
1032 out_guid(&entry
->type
);
1033 type_str
= type_guids_lookup_name(&entry
->type
);
1034 out_pf("(%s)", type_str
);
1036 out_guid(&entry
->uniq
);
1038 out_pf("0x%"PRIx64
" \"", entry
->attrs
);
1046 /* keep it simple */
1047 if ((u16
)'0' <= c
&& c
<= (u16
)'9'
1048 && (u16
)'A' <= c
&& c
<= (u16
)'Z'
1049 && (u16
)'a' <= c
&& c
<= (u16
)'z')
1057 STATIC
bool guid_is_zero(struct guid_t
*guid
)
1063 last
= b
+ sizeof(*guid
) - 1;
1072 STATIC
bool entry_is_used(u32 idx
)
1074 struct entry_t
*entry
;
1076 entry
= &entries
[idx
];
1077 if ( guid_is_zero(&entry
->type
)
1078 && guid_is_zero(&entry
->uniq
)
1079 && entry
->first
== 0
1081 && entry
->attrs
== 0
1082 && entry
->name
[0] == 0) {
1087 STATIC
void entry_delete(u32 idx
)
1089 struct entry_t
*entry
;
1093 entry
= &entries
[idx
];
1095 memset(entry
, 0, sizeof(*entry
));
1096 entry
->name
= utf16_strdup(&empty_str
);
1098 STATIC
void entries_show(void)
1102 if (entries_n
== 0) {
1103 out_pf("no partition entries\n");
1106 out_bold_pf("array of partition entries has %"PRIu32
" slots:\n", entries_n
);
1109 out_bold_pf("slot / first / last / type(guid) \n");
1110 out_bold_pf(" uniq_guid / attributes / name\n");
1112 if (entry_idx
== entries_n
)
1114 if (entry_is_used(entry_idx
))
1115 entry_show(entry_idx
, &entries
[entry_idx
]);
1119 STATIC
void main_menu_show(void)
1123 p print the partition entries\n\
1124 n new partition entry\n\
1125 d delete a partition entry\n\
1126 h print the header\n\
1127 u change disk guid\n\
1129 q quit without saving\n");
1131 STATIC u8 input_line
[BUFSIZ
];
1132 STATIC
void *(*input_state
)(size_t lf
);
1133 /* list of states -- start */
1134 STATIC
void *state_main_menu(size_t lf
);
1135 STATIC
void *state_delete_entry(size_t lf
);
1136 STATIC
void *state_change_disk_guid(size_t lf
);
1137 STATIC
void *state_edit_entry(size_t lf
);
1138 STATIC
void *state_edit_entry_substate_first_lba(size_t lf
);
1139 STATIC
void *state_edit_entry_substate_last_lba(size_t lf
);
1140 STATIC
void *state_edit_entry_substate_type(size_t lf
);
1141 STATIC
void *state_edit_entry_substate_uniq_quid(size_t lf
);
1142 STATIC
void *state_edit_entry_substate_attrs(size_t lf
);
1143 /* list of states -- end */
1146 u64 first_lba_default
;
1148 u64 last_lba_default
;
1155 STATIC
void edit_entry_attrs_prompt(void)
1157 out_pf("attributes as an hexadecimal number (default to 0x0): ");
1159 STATIC
void edit_entry_uniq_guid_prompt(void)
1161 out_pf("uniq guid (r to randomize): ");
1163 STATIC
void edit_entry_type_prompt(void)
1165 out_pf("number of the type of partition (l for the list): ");
1167 STATIC
void main_menu_prompt(void)
1169 out_pf("command (m for help): ");
1171 STATIC
void delete_entry_prompt(void)
1173 out_pf("number of the partition entry to delete: ");
1175 STATIC
void edit_entry_last_lba_prompt(void)
1177 out_pf("last logical block lba (default to %"PRIu64
"): ",
1178 edit_entry
.last_lba_default
);
1180 STATIC
void edit_entry_prompt(void)
1182 out_pf("partition entry number (1-%"PRIu32
"): ", entries_n
);
1184 STATIC
void edit_entry_first_lba_prompt(void)
1186 out_pf("first logical block lba (default to %"PRIu64
"): ",
1187 edit_entry
.first_lba_default
);
1189 STATIC
void change_disk_guid_prompt(void)
1191 out_pf("current disk guid is ");
1192 out_guid(&hdrs
.disk_guid
);
1193 out_pf("\nnew one (r to randomize): ");
1195 /*}}} prompts -- end */
1196 STATIC
void first_lba_default_select(void)
1200 /* mbr, header, entries. Should not need to cap this */
1201 edit_entry
.first_lba_default
= hdrs
.first_usable_lba
;
1202 if (edit_entry
.num
== 1)
1204 /* num >= 2, idx >= 1 */
1205 idx
= edit_entry
.num
- 2;
1207 * we are going "backward" to locate the right before used entry, then
1208 * we will use the last lba from this entry to compute the first
1212 if (entry_is_used(idx
)) {
1213 edit_entry
.first_lba_default
= entries
[idx
].last
+ 1;
1215 if (edit_entry
.first_lba_default
< hdrs
.first_usable_lba
)
1216 edit_entry
.first_lba_default
= hdrs
.first_usable_lba
;
1217 if (edit_entry
.first_lba_default
> hdrs
.last_usable_lba
)
1218 edit_entry
.first_lba_default
= hdrs
.last_usable_lba
;
1226 STATIC
void last_lba_default_select(void)
1230 edit_entry
.last_lba_default
= hdrs
.last_usable_lba
;
1231 if (edit_entry
.num
== entries_n
)
1233 /* num <= entries_n - 1, idx <= entries_n - 2 */
1234 idx
= edit_entry
.num
;
1236 * we are going forward to reach the first used entry and then use
1237 * the lba right before its first lba.
1240 if (idx
== entries_n
)
1242 if (entry_is_used(idx
)) {
1243 edit_entry
.last_lba_default
= entries
[idx
].first
- 1;
1245 if (edit_entry
.last_lba_default
< hdrs
.first_usable_lba
)
1246 edit_entry
.last_lba_default
= hdrs
.first_usable_lba
;
1247 if (edit_entry
.last_lba_default
> hdrs
.last_usable_lba
)
1248 edit_entry
.last_lba_default
= hdrs
.last_usable_lba
;
1254 STATIC
void *hdr_show(void)
1256 out_pf("header:\ndisk guid ");
1257 out_guid(&hdrs
.disk_guid
);
1258 out_pf("\nfirst usable lba %"PRIu64
"\n", hdrs
.first_usable_lba
);
1259 out_pf("last usable lba %"PRIu64
"\n", hdrs
.last_usable_lba
);
1260 out_pf("%s last lba is %"PRIu64
"\n", blk_dev
.path
, blk_dev
.last_lba
);
1262 STATIC
void guid_randomize(struct guid_t
*guid
)
1266 fd
= open("/dev/urandom", O_RDONLY
);
1268 warning_pf("unable to open /dev/urandom for guid randomization\n");
1271 if (!read_full(fd
, guid
, sizeof(*guid
)))
1272 warning_pf("something went wrong while reading from /dev/urandom, the guid is probably corrupted\n");
1276 STATIC
void guid_read_from_input(struct guid_t
*guid
, size_t lf
)
1284 end
= input_line
+ lf
;
1286 blk_start
= input_line
;
1287 blk_end
= strchr(blk_start
, '-');
1291 guid
->blk_0
= (u32
)strtou64(blk_start
, 0, 16);
1292 blk_start
= blk_end
+ 1;
1293 if (blk_start
>= end
)
1295 blk_end
= strchr(blk_start
, '-');
1299 guid
->blk_1
= (u16
)strtou64(blk_start
, 0, 16);
1300 blk_start
= blk_end
+ 1;
1301 if (blk_start
>= end
)
1303 blk_end
= strchr(blk_start
, '-');
1307 guid
->blk_2
= (u16
)strtou64(blk_start
, 0, 16);
1308 blk_start
= blk_end
+ 1;
1309 if (blk_start
>= end
)
1311 blk_end
= strchr(blk_start
, '-');
1315 guid
->blk_3
= (u16
)strtou64(blk_start
, 0, 16);
1317 blk_start
= blk_end
+ 1;
1318 if (blk_start
>= end
)
1320 blk_end
= blk_start
+ 12; /* 6 bytes */
1323 save
= blk_start
[2];
1325 guid
->blk_4
[0] = (u8
)strtou64(&blk_start
[0], 0, 16);
1326 blk_start
[2] = save
;
1328 save
= blk_start
[4];
1330 guid
->blk_4
[1] = (u8
)strtou64(&blk_start
[2], 0, 16);
1331 blk_start
[4] = save
;
1333 save
= blk_start
[6];
1335 guid
->blk_4
[2] = (u8
)strtou64(&blk_start
[4], 0, 16);
1336 blk_start
[6] = save
;
1338 save
= blk_start
[8];
1340 guid
->blk_4
[3] = (u8
)strtou64(&blk_start
[6], 0, 16);
1341 blk_start
[8] = save
;
1343 save
= blk_start
[10];
1345 guid
->blk_4
[4] = (u8
)strtou64(&blk_start
[8], 0, 16);
1346 blk_start
[10] = save
;
1348 guid
->blk_4
[5] = (u8
)strtou64(&blk_start
[10], 0, 16);
1350 STATIC
void *edit_entry_to_entries(void)
1352 struct entry_t
*dst_entry
;
1354 dst_entry
= &entries
[edit_entry
.num
- 1];
1355 memcpy(&dst_entry
->type
, &types
[edit_entry
.type
- 1].guid
,
1356 sizeof(dst_entry
->type
));
1357 memcpy(&dst_entry
->uniq
, &edit_entry
.uniq
, sizeof(dst_entry
->uniq
));
1358 dst_entry
->first
= edit_entry
.first_lba
;
1359 dst_entry
->last
= edit_entry
.last_lba
;
1360 dst_entry
->attrs
= edit_entry
.attrs
;
1361 /* don't support name input yet */
1363 STATIC
void gpt_write(void)
1365 entries_serialized_array_gen();
1366 /* crc32 on exactly the entries array size, not lba bounded */
1367 entries_serialized_array_crc32
= 0;
1368 le_crc32_update(entries_serialized_array
, entry_bytes_n
* entries_n
,
1369 &entries_serialized_array_crc32
);
1370 hdr_primary_serialized_gen();
1371 hdr_secondary_serialized_gen();
1372 protective_mbr_gen();
1373 protective_mbr_write();
1374 hdr_primary_serialized_write();
1375 hdr_secondary_serialized_write();
1376 entries_serialized_array_primary_write();
1377 entries_serialized_array_secondary_write();
1380 STATIC
void *state_edit_entry_substate_attrs(size_t lf
)
1383 edit_entry
.attrs
= 0;
1387 edit_entry
.attrs
= strtou64(input_line
, 0, 16);
1390 * TODO: don't want to use iconv, will implement an utf8 to utf16
1391 * converter another time.
1393 edit_entry_to_entries();
1395 return state_main_menu
;
1397 STATIC
void *state_edit_entry_substate_uniq_quid(size_t lf
)
1399 if (lf
== 1 && input_line
[0] == 'r') {
1400 guid_randomize(&edit_entry
.uniq
);
1401 out_pf("generated guid is : ");
1404 guid_read_from_input(&edit_entry
.uniq
, lf
);
1405 out_pf("read guid is : ");
1407 out_guid(&edit_entry
.uniq
);
1409 edit_entry_attrs_prompt();
1410 return state_edit_entry_substate_attrs
;
1412 STATIC
void *state_edit_entry_substate_type(size_t lf
)
1414 if (lf
== 1 && input_line
[0] == 'l') {
1416 edit_entry_type_prompt();
1417 return state_edit_entry_substate_type
;
1420 edit_entry
.type
= (u32
)strtou64(input_line
, 0, 10);
1421 if (edit_entry
.type
== 0 || edit_entry
.type
> ARRAY_N(types
)) {
1422 warning_pf("invalid type number\n");
1423 edit_entry_type_prompt();
1424 return state_edit_entry_substate_type
;
1426 edit_entry_uniq_guid_prompt();
1427 return state_edit_entry_substate_uniq_quid
;
1429 STATIC
void *state_edit_entry_substate_last_lba(size_t lf
)
1432 edit_entry
.last_lba
= edit_entry
.last_lba_default
;
1435 edit_entry
.last_lba
= strtou64(input_line
, 0, 10);
1437 edit_entry_type_prompt();
1438 return state_edit_entry_substate_type
;
1440 STATIC
void *state_edit_entry_substate_first_lba(size_t lf
)
1443 edit_entry
.first_lba
= edit_entry
.first_lba_default
;
1446 edit_entry
.first_lba
= strtou64(input_line
, 0, 10);
1448 last_lba_default_select();
1449 edit_entry_last_lba_prompt();
1450 return state_edit_entry_substate_last_lba
;
1452 STATIC
void *state_edit_entry(size_t lf
)
1455 edit_entry
.num
= (u32
)strtou64(input_line
, 0, 10);
1456 if (1 <= edit_entry
.num
&& edit_entry
.num
<= entries_n
) {
1457 first_lba_default_select();
1458 edit_entry_first_lba_prompt();
1459 return state_edit_entry_substate_first_lba
;
1461 warning_pf("%"PRIu32
" is an invalid partition entry number\n", edit_entry
.num
);
1463 return state_main_menu
;
1465 STATIC
void *state_delete_entry(size_t lf
)
1471 entry_num
= (u32
)strtou64(input_line
, 0, 10);
1472 if (1 <= entry_num
&& entry_num
<= entries_n
) {
1473 out_bold_pf("deleting partition entry %"PRIu64
"\n", entry_num
);
1474 entry_delete((u32
)(entry_num
- 1));
1476 out_bold_pf("partition number out of range %"PRIu64
"\n", entry_num
);
1478 return state_main_menu
;
1480 STATIC
void *state_change_disk_guid(size_t lf
)
1483 if (input_line
[0] == 'r') {
1484 guid_randomize(&hdrs
.disk_guid
);
1485 out_pf("randomized disk guid is ");
1488 guid_read_from_input(&hdrs
.disk_guid
, lf
);
1489 out_pf("read disk guid is ");
1491 out_guid(&hdrs
.disk_guid
);
1494 return state_main_menu
;
1496 STATIC
void *state_main_menu(size_t lf
)
1499 switch(input_line
[0]) {
1513 delete_entry_prompt();
1514 return state_delete_entry
;
1516 edit_entry_prompt();
1517 return state_edit_entry
;
1519 change_disk_guid_prompt();
1520 return state_change_disk_guid
;
1529 return state_main_menu
;
1531 /*}}} states -- end */
1532 /* end is the index of the byte past the last read byte */
1533 STATIC
size_t input_line_consume(size_t lf
, size_t end
)
1535 input_state
= input_state(lf
);
1537 /* update the input line buffer and offsets */
1538 if (lf
== (BUFSIZ
- 1)) { /* lf is the last char in our line buffer */
1539 memset(input_line
, 0, BUFSIZ
);
1542 memmove(input_line
, input_line
+ lf
+ 1, end
- (lf
+ 1));
1543 return end
- (lf
+ 1);
1545 STATIC
void input_line_loop(void)
1551 memset(input_line
, 0, BUFSIZ
);
1553 input_state
= state_main_menu
;
1560 r
= read(0, &input_line
[end
], BUFSIZ
- end
);
1564 error_pf("error reading input line from standard input\n");
1568 loop
{ /* scan what was read for lf */
1571 if (input_line
[lf
] == '\n') {
1577 end
= input_line_consume(lf
, end
);
1582 if (end
== BUFSIZ
) {
1584 out_pf("your input line is too long, discarding...\n");
1591 STATIC
void init_once(void)
1595 load_previous_gpt
= true;
1598 memset(&hdrs
, 0, sizeof(hdrs
));
1600 STATIC
void usage(void)
1604 nyangpt: line input oriented minimal GPT partition creator\n\
1607 nyangpt [-nc] [-nl] [-h|--help][ [--] device_path\n\
1609 -nc: disable ANSI terminal colors\n\
1610 -nl: don't load previous GPT partitions\n\
1611 -h|--help: this summary\n");
1615 STATIC
void options_parse(u32 argc
, utf8
**argv
)
1618 bool no_more_options
;
1624 no_more_options
= false;
1625 print_usage
= false;
1630 if (no_more_options
) {
1631 if (blk_dev
.path
== 0)
1632 blk_dev
.path
= argv
[arg
];
1633 } else if (strcmp(argv
[arg
], "--") == 0) {
1634 no_more_options
= true;
1635 } else if (strcmp(argv
[arg
], "-nc") == 0) {
1637 } else if (strcmp(argv
[arg
], "-nl") == 0) {
1638 load_previous_gpt
= false;
1639 } else if (strcmp(argv
[arg
], "-h") == 0
1640 || strcmp(argv
[arg
], "--help") == 0) {
1643 if (blk_dev
.path
== 0)
1644 blk_dev
.path
= argv
[arg
];
1648 if (print_usage
|| blk_dev
.path
== 0)
1651 int nyangpt_main(int argc
, utf8
**argv
)
1653 u64 whole_dev_bytes_n
;
1654 u64 entries_bytes_n
;
1657 options_parse(argc
, argv
);
1658 out_pf("block device path is %s, opening...\n", blk_dev
.path
);
1659 blk_dev
.fd
= open(blk_dev
.path
, O_RDWR
| O_SYNC
);
1660 if (blk_dev
.fd
== -1)
1661 error_pf("%s:unable to open\n", blk_dev
.path
);
1662 out_pf("%s:opened\n", blk_dev
.path
);
1666 whole_dev_bytes_n
= blk_dev
.sz_512_n
* 512;
1667 if ((whole_dev_bytes_n
% blk_dev
.logical_blk_bytes_n
) != 0)
1668 error_pf("%s: the whole device size %"PRIu64
" is not a multiple of the logical block size %"PRIu64
" bytes\n", whole_dev_bytes_n
, blk_dev
.logical_blk_bytes_n
);
1669 /* the total number of lba, -1 to get the offset of the last one */
1670 blk_dev
.last_lba
= blk_dev
.sz_512_n
* 512 / blk_dev
.logical_blk_bytes_n
- 1;
1671 out_pf("%s:last lba is %"PRIu64
"\n", blk_dev
.path
, blk_dev
.last_lba
);
1673 if (load_previous_gpt
) {
1674 previous_gpt_load();
1675 build_data_from_previous_gpt();
1678 * Resizing the array of entries is expensive and dangerous, try to
1679 * keep it constant as much as possible.
1680 * If it was not initialized based from the previous gpt, init one
1681 * with sane defaults.
1685 if (hdrs
.bytes_n
== 0)
1686 hdrs
.bytes_n
= HDR_BYTES_N
;
1687 if (guid_is_zero(&hdrs
.disk_guid
))
1688 guid_randomize(&hdrs
.disk_guid
);
1690 * Once we have "an array of entries", the first and last usable
1691 * lbas must be recomputed.
1692 * We ignore the values from the previous gpt headers, we prefer
1693 * to recompute them.
1695 hdrs_usable_lbas_compute();
1699 /*{{{ preprocessor space cleanup */
1710 #undef BOOT_SIGNATURE_0X55
1711 #undef BOOT_SIGNATURE_0XAA
1712 #undef ENTRIES_ARRAY_MIN_BYTES_N
1713 #undef ENTRY_BYTES_N
1716 #undef HDR_SIGNATURE
1717 #undef HDR_SIGNATURE_BYTES_N
1729 #undef X64_UTF8_BYTES_MAX
1730 /*}}} preprocessor space cleanup -- end */
1731 /*{{{ namespace cleanup ------------------------------------------------------*/
1733 #undef build_data_from_previous_gpt
1734 #undef change_disk_guid_prompt
1736 #undef delete_entry_prompt
1738 #undef edit_entry_attrs_prompt
1739 #undef edit_entry_first_lba_prompt
1740 #undef edit_entry_last_lba_prompt
1741 #undef edit_entry_prompt
1742 #undef edit_entry_to_entries
1743 #undef edit_entry_type_prompt
1744 #undef edit_entry_uniq_guid_prompt
1746 #undef entries_lbas_n
1749 #undef entries_reset
1750 #undef entries_serialized_array
1751 #undef entries_serialized_array_crc32
1752 #undef entries_serialized_array_gen
1753 #undef entries_serialized_array_gen_entry
1754 #undef entries_serialized_array_primary_write
1755 #undef entries_serialized_array_secondary_write
1757 #undef entry_bytes_n
1759 #undef entry_is_used
1763 #undef first_lba_default_select
1767 #undef guid_randomize
1769 #undef guid_read_from_input
1773 #undef hdr_primary_serialized
1774 #undef hdr_primary_serialized_gen
1775 #undef hdr_primary_serialized_write
1776 #undef hdr_secondary_serialized
1777 #undef hdr_secondary_serialized_gen
1778 #undef hdr_secondary_serialized_write
1782 #undef hdrs_usable_lbas_compute
1785 #undef input_line_consume
1786 #undef input_line_loop
1788 #undef last_lba_default_select
1789 #undef le_crc32_tbl_gen
1790 #undef le_crc32_update
1791 #undef load_previous_gpt
1792 #undef main_menu_prompt
1793 #undef main_menu_show
1794 #undef options_parse
1799 #undef protective_mbr
1800 #undef protective_mbr_gen
1801 #undef protective_mbr_write
1803 #undef state_change_disk_guid
1804 #undef state_delete_entry
1805 #undef state_edit_entry
1806 #undef state_edit_entry_substate_attrs
1807 #undef state_edit_entry_substate_first_lba
1808 #undef state_edit_entry_substate_last_lba
1809 #undef state_edit_entry_substate_type
1810 #undef state_edit_entry_substate_uniq_quid
1811 #undef state_main_menu
1812 #undef sysfs_infos_get
1813 #undef type_guids_lookup_name
1820 /*----------------------------------------------------------------------------*/
1822 /*}}} namespace cleanup -- end -----------------------------------------------*/